Skip to content

Commit c16f12b

Browse files
committed
Poll for port/volume status when attached but not yet transitioned
After a port is attached to a server, its Neutron status transitions asynchronously from DOWN to ACTIVE. Similarly, a volume transitions from available to in-use after attachment. The cross-controller signaling via serverTo{Port,Volume}MapFunc is susceptible to a race with the controller's own status write. Add defense-in-depth polling: when the port has a device_id but status is still DOWN, or when a volume has attachments but status is not in-use, schedule a re-fetch via WaitingOnOpenStack. This ensures the controller picks up the status transition regardless of whether the cross-controller signal is lost.
1 parent d2cd4a7 commit c16f12b

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

internal/controllers/port/actuator.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,21 @@ func (actuator portActuator) checkAttachedServer(ctx context.Context, obj orcObj
396396
}
397397
}
398398

399+
// When the port is attached to a device but still reports DOWN,
400+
// the Neutron status has not yet transitioned to ACTIVE (e.g.
401+
// OVN is still binding the port). Schedule a re-fetch so we
402+
// pick up the ACTIVE status without relying solely on the
403+
// cross-controller serverToPortMapFunc signal, which is
404+
// susceptible to a race with the port controller's own status
405+
// write.
406+
if osResource.Status == PortStatusDown {
407+
log.V(logging.Verbose).Info("Port is attached but status is still DOWN, polling for status change",
408+
"port", obj.Name,
409+
"deviceID", osResource.DeviceID,
410+
"status", osResource.Status)
411+
return progress.WaitingOnOpenStack(progress.WaitingOnReady, serverBuildPollingPeriod)
412+
}
413+
399414
return nil
400415
}
401416

internal/controllers/volume/actuator.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,29 @@ func handleDescriptionUpdate(updateOpts *volumes.UpdateOpts, resource *resourceS
283283

284284
func (actuator volumeActuator) GetResourceReconcilers(ctx context.Context, orcObject orcObjectPT, osResource *osResourceT, controller interfaces.ResourceController) ([]resourceReconciler, progress.ReconcileStatus) {
285285
return []resourceReconciler{
286+
actuator.checkAttachmentStatus,
286287
actuator.updateResource,
287288
}, nil
288289
}
289290

291+
func (volumeActuator) checkAttachmentStatus(ctx context.Context, _ orcObjectPT, osResource *osResourceT) progress.ReconcileStatus {
292+
log := ctrl.LoggerFrom(ctx)
293+
294+
// When the volume has attachments but Cinder still reports
295+
// "available" rather than "in-use", the status transition has
296+
// not completed yet. Schedule a re-fetch so we pick up the
297+
// updated status without relying solely on the cross-controller
298+
// serverToVolumeMapFunc signal, which is susceptible to a race
299+
// with the volume controller's own status write.
300+
if len(osResource.Attachments) > 0 && osResource.Status != VolumeStatusInUse {
301+
log.V(logging.Verbose).Info("Volume has attachments but status is not in-use, polling for status change",
302+
"status", osResource.Status)
303+
return progress.WaitingOnOpenStack(progress.WaitingOnReady, volumeAvailablePollingPeriod)
304+
}
305+
306+
return nil
307+
}
308+
290309
type volumeHelperFactory struct{}
291310

292311
var _ helperFactory = volumeHelperFactory{}

0 commit comments

Comments
 (0)