@@ -373,9 +373,11 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
373373 }
374374
375375 // Find server with matching ID
376+ found := false
376377 for i := range serverList .Items {
377378 server := & serverList .Items [i ]
378379 if server .Status .ID != nil && * server .Status .ID == osResource .DeviceID {
380+ found = true
379381 // Check if server is in BUILD status
380382 if server .Status .Resource != nil && server .Status .Resource .Status == "BUILD" {
381383 log .V (logging .Verbose ).Info ("Port is attached to server in BUILD status, waiting" ,
@@ -389,6 +391,23 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
389391 }
390392 }
391393
394+ // When the port is attached to a device but still reports DOWN,
395+ // the Neutron status has not yet transitioned to ACTIVE (e.g.
396+ // OVN is still binding the port). serverToPortMapFunc also
397+ // detects this and triggers a reconcile, but it races with the
398+ // port controller's own status write: the controller may
399+ // overwrite Progressing=True with Progressing=False before the
400+ // port becomes ACTIVE. Poll here so we keep Progressing=True
401+ // until the transition completes. Only do this when we found
402+ // the ORC Server object the port is attached to, to avoid
403+ // unnecessary polling when the device isn't a tracked server.
404+ if found && osResource .Status == PortStatusDown {
405+ log .V (logging .Verbose ).Info ("port needs reconciliation: attached to server but status is DOWN" ,
406+ "port" , obj .Name ,
407+ "status" , osResource .Status )
408+ return progress .WaitingOnOpenStack (progress .WaitingOnReady , serverBuildPollingPeriod )
409+ }
410+
392411 return nil
393412}
394413
0 commit comments