@@ -380,9 +380,11 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
380380 }
381381
382382 // Find server with matching ID
383+ found := false
383384 for i := range serverList .Items {
384385 server := & serverList .Items [i ]
385386 if server .Status .ID != nil && * server .Status .ID == osResource .DeviceID {
387+ found = true
386388 // Check if server is in BUILD status
387389 if server .Status .Resource != nil && server .Status .Resource .Status == "BUILD" {
388390 log .V (logging .Verbose ).Info ("Port is attached to server in BUILD status, waiting" ,
@@ -396,6 +398,23 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
396398 }
397399 }
398400
401+ // When the port is attached to a device but still reports DOWN,
402+ // the Neutron status has not yet transitioned to ACTIVE (e.g.
403+ // OVN is still binding the port). serverToPortMapFunc also
404+ // detects this and triggers a reconcile, but it races with the
405+ // port controller's own status write: the controller may
406+ // overwrite Progressing=True with Progressing=False before the
407+ // port becomes ACTIVE. Poll here so we keep Progressing=True
408+ // until the transition completes. Only do this when we found
409+ // the ORC Server object the port is attached to, to avoid
410+ // unnecessary polling when the device isn't a tracked server.
411+ if found && osResource .Status == PortStatusDown {
412+ log .V (logging .Verbose ).Info ("port needs reconciliation: attached to server but status is DOWN" ,
413+ "port" , obj .Name ,
414+ "status" , osResource .Status )
415+ return progress .WaitingOnOpenStack (progress .WaitingOnReady , serverBuildPollingPeriod )
416+ }
417+
399418 return nil
400419}
401420
0 commit comments