@@ -24,7 +24,6 @@ import (
2424 "time"
2525
2626 "github.com/go-logr/logr"
27- "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes"
2827 "k8s.io/apimachinery/pkg/api/equality"
2928 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3029 "k8s.io/apimachinery/pkg/runtime"
@@ -37,30 +36,24 @@ import (
3736 "sigs.k8s.io/controller-runtime/pkg/predicate"
3837
3938 v1alpha1 "github.com/osac-project/bare-metal-fulfillment-operator/api/v1alpha1"
40- "github.com/osac-project/host-management-openstack/internal/ironic "
39+ "github.com/osac-project/host-management-openstack/internal/management "
4140 opv1alpha1 "github.com/osac-project/osac-operator/api/v1alpha1"
4241 "github.com/osac-project/osac-operator/pkg/provisioning"
4342)
4443
45- // HostLeaseReconciler reconciles HostLease CRs for power management via Ironic.
4644type HostLeaseReconciler struct {
4745 client.Client
48- Scheme * runtime.Scheme
49- IronicClient ironic.NodeClient
50- ProvisioningProvider provisioning.ProvisioningProvider
51-
52- // RecheckInterval is the interval for polling Ironic until power state matches desired state.
53- RecheckInterval time.Duration
54-
55- // ProvisionPollInterval is the interval for polling AAP job status.
46+ Scheme * runtime.Scheme
47+ ManagementClient management.Client
48+ ProvisioningProvider provisioning.ProvisioningProvider
49+ RecheckInterval time.Duration
5650 ProvisionPollInterval time.Duration
5751}
5852
59- // NewHostLeaseReconciler creates a new HostLeaseReconciler with defaults applied.
6053func NewHostLeaseReconciler (
6154 client client.Client ,
6255 scheme * runtime.Scheme ,
63- ironicClient ironic. NodeClient ,
56+ managementClient management. Client ,
6457 provider provisioning.ProvisioningProvider ,
6558 recheckInterval time.Duration ,
6659) * HostLeaseReconciler {
@@ -71,7 +64,7 @@ func NewHostLeaseReconciler(
7164 return & HostLeaseReconciler {
7265 Client : client ,
7366 Scheme : scheme ,
74- IronicClient : ironicClient ,
67+ ManagementClient : managementClient ,
7568 ProvisioningProvider : provider ,
7669 RecheckInterval : recheckInterval ,
7770 ProvisionPollInterval : DefaultProvisionPollInterval ,
@@ -140,33 +133,32 @@ func (r *HostLeaseReconciler) handleUpdate(ctx context.Context, hostLease *v1alp
140133 }
141134 }
142135
143- node , err := r .IronicClient . GetNode (ctx , hostLease .Spec .ExternalHostID )
136+ powerStatus , err := r .ManagementClient . GetPowerState (ctx , hostLease .Spec .ExternalHostID )
144137 if err != nil {
145- log .Error (err , "failed to get Ironic node " , "nodeID" , hostLease .Spec .ExternalHostID )
138+ log .Error (err , "failed to get power state " , "nodeID" , hostLease .Spec .ExternalHostID )
146139 r .syncHostLeaseStatus (hostLease , nil , err , log )
147140 return ctrl.Result {}, err
148141 }
149- log .V (1 ).Info ("Ironic node " , "nodeID" , hostLease .Spec .ExternalHostID , "power_state" , node . PowerState )
142+ log .V (1 ).Info ("Host power state " , "nodeID" , hostLease .Spec .ExternalHostID , "power_state" , powerStatus . State )
150143
151144 if hostLease .Spec .PoweredOn != nil {
152- if err := r .reconcilePower (ctx , hostLease , node , log ); err != nil {
145+ if err := r .reconcilePower (ctx , hostLease , powerStatus , log ); err != nil {
153146 r .syncHostLeaseStatus (hostLease , nil , err , log )
154147 return ctrl.Result {}, err
155148 }
156149
157- node , err = r .IronicClient . GetNode (ctx , hostLease .Spec .ExternalHostID )
150+ powerStatus , err = r .ManagementClient . GetPowerState (ctx , hostLease .Spec .ExternalHostID )
158151 if err != nil {
159- log .Error (err , "failed to refresh node after power reconciliation" , "nodeID" , hostLease .Spec .ExternalHostID )
152+ log .Error (err , "failed to refresh power state after reconciliation" , "nodeID" , hostLease .Spec .ExternalHostID )
160153 r .syncHostLeaseStatus (hostLease , nil , err , log )
161154 return ctrl.Result {}, err
162155 }
163156 }
164157
165- r .syncHostLeaseStatus (hostLease , node , nil , log )
158+ r .syncHostLeaseStatus (hostLease , powerStatus , nil , log )
166159
167160 if hostLease .Spec .PoweredOn != nil {
168- currentlyOn := node .PowerState == ironic .PowerOn .String ()
169- if * hostLease .Spec .PoweredOn != currentlyOn {
161+ if * hostLease .Spec .PoweredOn != (powerStatus .State == management .PowerOn ) {
170162 return ctrl.Result {RequeueAfter : r .RecheckInterval }, nil
171163 }
172164 }
@@ -292,38 +284,41 @@ func (r *HostLeaseReconciler) validateOpenStackHost(hostLease *v1alpha1.HostLeas
292284 return true
293285}
294286
295- func (r * HostLeaseReconciler ) reconcilePower (ctx context.Context , hostLease * v1alpha1.HostLease , node * nodes. Node , log logr.Logger ) error {
296- currentlyOn := node . PowerState == ironic .PowerOn . String ()
287+ func (r * HostLeaseReconciler ) reconcilePower (ctx context.Context , hostLease * v1alpha1.HostLease , powerStatus * management. PowerStatus , log logr.Logger ) error {
288+ currentlyOn := powerStatus . State == management .PowerOn
297289 desiredOn := * hostLease .Spec .PoweredOn
298290
299- // If Ironic is already processing a power state change, skip to avoid 409 Conflict.
300- if r .IronicClient .IsNodePowerTransitioning (node ) {
291+ if powerStatus .IsTransitioning {
301292 log .V (1 ).Info ("Node is transitioning, skipping power action" ,
302- "nodeID" , hostLease .Spec .ExternalHostID ,
303- "targetPowerState" , node .TargetPowerState )
293+ "nodeID" , hostLease .Spec .ExternalHostID )
304294 return nil
305295 }
306296
307- var err error
308297 needsPowerUpdate := desiredOn != currentlyOn
309298 if ! needsPowerUpdate {
310- log .Info ("Power state already matches desired" , "poweredOn" , desiredOn , "power_state" , node . PowerState )
299+ log .Info ("Power state already matches desired" , "poweredOn" , desiredOn , "power_state" , powerStatus . State )
311300 return nil
312301 }
313302
314- targetState := ironic .PowerOff
303+ targetState := management .PowerOff
315304 action := "off"
316305 if desiredOn {
317- targetState = ironic .PowerOn
306+ targetState = management .PowerOn
318307 action = "on"
319308 }
320309
321310 log .Info ("Powering " + action + " node" , "nodeID" , hostLease .Spec .ExternalHostID )
322- if err = r .IronicClient .SetPowerState (ctx , hostLease .Spec .ExternalHostID , targetState ); err != nil {
311+ if err := r .ManagementClient .SetPowerState (ctx , hostLease .Spec .ExternalHostID , targetState ); err != nil {
312+ if errors .Is (err , management .ErrTransitioning ) {
313+ log .Info ("Node is transitioning (conflict), will retry" ,
314+ "nodeID" , hostLease .Spec .ExternalHostID )
315+ return nil
316+ }
323317 log .Error (err , "failed to power " + action + " node" , "nodeID" , hostLease .Spec .ExternalHostID )
318+ return err
324319 }
325320
326- return err
321+ return nil
327322}
328323
329324func (r * HostLeaseReconciler ) reconcileProvisioning (ctx context.Context , hostLease * v1alpha1.HostLease ) (ctrl.Result , error ) {
@@ -382,8 +377,7 @@ func (r *HostLeaseReconciler) reconcileProvisioning(ctx context.Context, hostLea
382377 return result , nil
383378}
384379
385- // syncHostLeaseStatus syncs power-related conditions and observed power state in memory.
386- func (r * HostLeaseReconciler ) syncHostLeaseStatus (hostLease * v1alpha1.HostLease , node * nodes.Node , reconcileErr error , log logr.Logger ) {
380+ func (r * HostLeaseReconciler ) syncHostLeaseStatus (hostLease * v1alpha1.HostLease , powerStatus * management.PowerStatus , reconcileErr error , log logr.Logger ) {
387381 if reconcileErr != nil {
388382 hostLease .Status .Phase = v1alpha1 .HostLeasePhaseFailed
389383 hostLease .SetStatusCondition (
@@ -396,11 +390,11 @@ func (r *HostLeaseReconciler) syncHostLeaseStatus(hostLease *v1alpha1.HostLease,
396390 return
397391 }
398392
399- if node == nil {
393+ if powerStatus == nil {
400394 return
401395 }
402396
403- poweredOn := node . PowerState == ironic .PowerOn . String ()
397+ poweredOn := powerStatus . State == management .PowerOn
404398 hostLease .Status .PoweredOn = & poweredOn
405399
406400 if hostLease .Spec .PoweredOn != nil && * hostLease .Spec .PoweredOn != poweredOn {
0 commit comments