@@ -1527,23 +1527,27 @@ func (p *ironicProvisioner) Delete(ctx context.Context) (result provisioner.Resu
15271527 "target" , ironicNode .TargetProvisionState ,
15281528 "deploy step" , ironicNode .DeployStep ,
15291529 )
1530+ return p .realDelete (ctx , ironicNode , false )
1531+ }
15301532
1533+ func (p * ironicProvisioner ) realDelete (ctx context.Context , ironicNode * nodes.Node , force bool ) (result provisioner.Result , err error ) {
15311534 currentProvState := nodes .ProvisionState (ironicNode .ProvisionState )
15321535
1533- // Handle verifying state specially: Ironic holds an exclusive lock during
1534- // verification, so we can't set maintenance mode or delete until it completes.
1535- // Just wait for the verification to finish (success or timeout).
1536- if currentProvState == nodes .Verifying {
1536+ switch currentProvState {
1537+ case nodes .Verifying :
1538+ // Handle verifying state specially: Ironic holds an exclusive lock during
1539+ // verification, so we can't set maintenance mode or delete until it completes.
1540+ // Just wait for the verification to finish (success or timeout).
15371541 p .log .Info ("node is verifying, waiting for verification to complete before deletion" )
15381542 return operationContinuing (provisionRequeueDelay )
1539- }
15401543
1541- // For enroll state, the node can be deleted directly without maintenance mode
1542- // since it has no Nova associations and isn't locked.
1543- if currentProvState == nodes . Enroll {
1544+ case nodes . Enroll :
1545+ // For enroll state, the node can be deleted directly without maintenance mode
1546+ // since it has no Nova associations and isn't locked.
15441547 p .log .Info ("node is in enroll state, proceeding to delete directly" )
15451548 // Fall through to deletion
1546- } else if currentProvState == nodes .Available || currentProvState == nodes .Manageable {
1549+
1550+ case nodes .Available , nodes .Manageable :
15471551 // Make sure we don't have a stale instance UUID
15481552 if ironicNode .InstanceUUID != "" {
15491553 var success bool
@@ -1555,20 +1559,51 @@ func (p *ironicProvisioner) Delete(ctx context.Context) (result provisioner.Resu
15551559 return result , err
15561560 }
15571561 }
1558- } else if ! ironicNode .Maintenance {
1559- // If we see an active node and the controller doesn't think
1560- // we need to deprovision it, that means the node was
1561- // ExternallyProvisioned and we should remove it from Ironic
1562- // without deprovisioning it.
1563- //
1564- // If we see a node with an error, we will have to set the
1565- // maintenance flag before deleting it.
1566- //
1567- // Any other state requires us to use maintenance mode to
1568- // delete while bypassing Ironic's internal checks related to
1569- // Nova.
1570- p .log .Info ("setting host maintenance flag to force image delete" )
1571- return p .setMaintenanceFlag (ctx , ironicNode , true , "forcing deletion in baremetal-operator" )
1562+
1563+ case nodes .Deploying , nodes .Cleaning , nodes .Inspecting , nodes .Servicing , nodes .Deleting :
1564+ p .log .Info ("node is in state that does not allow deletion, waiting" , "currentState" , currentProvState )
1565+ return operationContinuing (provisionRequeueDelay )
1566+
1567+ case nodes .DeployWait :
1568+ if force && ! p .availableFeatures .HasDeploymentAbort () {
1569+ p .log .Info ("deprovisioning to force deletion" )
1570+ // No new API - fall back to deprovisioning and wait for CLEANWAIT
1571+ return p .changeNodeProvisionState (ctx , ironicNode ,
1572+ nodes.ProvisionStateOpts {Target : nodes .TargetDeleted },
1573+ )
1574+ }
1575+
1576+ // Otherwise, use the abort API as well
1577+ fallthrough
1578+
1579+ case nodes .CleanWait , nodes .ServiceWait :
1580+ if force {
1581+ p .log .Info ("aborting the current operation to force deletion" , "currentState" , currentProvState )
1582+ return p .changeNodeProvisionState (ctx , ironicNode ,
1583+ nodes.ProvisionStateOpts {Target : nodes .TargetAbort },
1584+ )
1585+ }
1586+
1587+ // Normal deletion won't work in these states, so wait
1588+ p .log .Info ("node is in state that does not allow deletion, waiting" , "currentState" , currentProvState )
1589+ return operationContinuing (provisionRequeueDelay )
1590+
1591+ default :
1592+ if ! ironicNode .Maintenance {
1593+ // If we see an active node and the controller doesn't think
1594+ // we need to deprovision it, that means the node was
1595+ // ExternallyProvisioned and we should remove it from Ironic
1596+ // without deprovisioning it.
1597+ //
1598+ // If we see a node with an error, we will have to set the
1599+ // maintenance flag before deleting it.
1600+ //
1601+ // Any other state requires us to use maintenance mode to
1602+ // delete while bypassing Ironic's internal checks related to
1603+ // Nova.
1604+ p .log .Info ("setting host maintenance flag to force image delete" , "currentState" , currentProvState )
1605+ return p .setMaintenanceFlag (ctx , ironicNode , true , "forcing deletion in baremetal-operator" )
1606+ }
15721607 }
15731608
15741609 p .log .Info ("host ready to be removed" )
@@ -1592,10 +1627,26 @@ func (p *ironicProvisioner) Delete(ctx context.Context) (result provisioner.Resu
15921627// for the target system. It may be called multiple times,
15931628// and should return true for its dirty flag until the
15941629// deletion operation is completed.
1595- func (p * ironicProvisioner ) Detach (ctx context.Context ) (result provisioner.Result , err error ) {
1630+ func (p * ironicProvisioner ) Detach (ctx context.Context , force bool ) (result provisioner.Result , err error ) {
15961631 // Currently the same behavior as Delete()
1597- p .log .Info ("removing the node for detachment" , "node" , p .nodeID )
1598- return p .Delete (ctx )
1632+ ironicNode , err := p .getNode (ctx )
1633+ if err != nil {
1634+ if errors .Is (err , provisioner .ErrNeedsRegistration ) {
1635+ p .log .Info ("no node found, already deleted" )
1636+ return operationComplete ()
1637+ }
1638+ return transientError (err )
1639+ }
1640+
1641+ p .log .Info ("deleting host for detachment" ,
1642+ "ID" , ironicNode .UUID ,
1643+ "lastError" , ironicNode .LastError ,
1644+ "current" , ironicNode .ProvisionState ,
1645+ "target" , ironicNode .TargetProvisionState ,
1646+ "deploy step" , ironicNode .DeployStep ,
1647+ "force" , force ,
1648+ )
1649+ return p .realDelete (ctx , ironicNode , force )
15991650}
16001651
16011652// softPowerOffUnsupportedError is returned when the BMC does not
0 commit comments