Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion apis/metal3.io/v1alpha1/baremetalhost_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ const (
// an immediate requeue)
PausedAnnotation = "baremetalhost.metal3.io/paused"

// Detached is the annotation which stops provisioner management of the host
// unlike in the paused case, the host status may be updated
DetachedAnnotation = "baremetalhost.metal3.io/detached"

// StatusAnnotation is the annotation that keeps a copy of the Status of BMH
// This is particularly useful when we pivot BMH. If the status
// annotation is present and status is empty, BMO will reconstruct BMH Status
Expand Down Expand Up @@ -121,6 +125,10 @@ const (
// OperationalStatusDelayed is the status value for when the host
// deployment needs to be delayed to limit simultaneous hosts provisioning
OperationalStatusDelayed = "delayed"

// OperationalStatusDetached is the status value when the host is
// marked unmanaged via the detached annotation
OperationalStatusDetached OperationalStatus = "detached"
)

// ErrorType indicates the class of problem that has caused the Host resource
Expand All @@ -147,6 +155,9 @@ const (
// PowerManagementError is an error condition occurring when the
// controller is unable to modify the power state of the Host.
PowerManagementError ErrorType = "power management error"
// DetachError is an error condition occurring when the
// controller is unable to detatch the host from the provisioner
DetachError ErrorType = "detach error"
)

// ProvisioningState defines the states the provisioner will report
Expand Down Expand Up @@ -625,7 +636,7 @@ type BareMetalHostStatus struct {
// after modifying this file

// OperationalStatus holds the status of the host
// +kubebuilder:validation:Enum="";OK;discovered;error;delayed
// +kubebuilder:validation:Enum="";OK;discovered;error;delayed;detached
OperationalStatus OperationalStatus `json:"operationalStatus"`

// ErrorType indicates the type of failure encountered when the
Expand Down
1 change: 1 addition & 0 deletions config/crd/bases/metal3.io_baremetalhosts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ spec:
- discovered
- error
- delayed
- detached
type: string
poweredOn:
description: indicator for whether or not the host is powered on
Expand Down
1 change: 1 addition & 0 deletions config/render/capm3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ spec:
- discovered
- error
- delayed
- detached
type: string
poweredOn:
description: indicator for whether or not the host is powered on
Expand Down
28 changes: 28 additions & 0 deletions controllers/metal3.io/baremetalhost_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ func recordActionFailure(info *reconcileInfo, errorType metal3v1alpha1.ErrorType
setErrorMessage(info.host, errorType, errorMessage)

eventType := map[metal3v1alpha1.ErrorType]string{
metal3v1alpha1.DetachError: "DetachError",
metal3v1alpha1.ProvisionedRegistrationError: "ProvisionedRegistrationError",
metal3v1alpha1.RegistrationError: "RegistrationError",
metal3v1alpha1.InspectionError: "InspectionError",
Expand Down Expand Up @@ -539,6 +540,33 @@ func getCurrentImage(host *metal3v1alpha1.BareMetalHost) *metal3v1alpha1.Image {
return nil
}

// detachHost() detaches the host from the Provisioner
func (r *BareMetalHostReconciler) detachHost(prov provisioner.Provisioner, info *reconcileInfo) actionResult {
provResult, err := prov.Detach()
if err != nil {
return actionError{errors.Wrap(err, "failed to detach")}
}
if provResult.ErrorMessage != "" {
return recordActionFailure(info, metal3v1alpha1.DetachError, provResult.ErrorMessage)
Comment thread
hardys marked this conversation as resolved.
}
if provResult.Dirty {
Comment thread
hardys marked this conversation as resolved.
if info.host.Status.ErrorType == metal3v1alpha1.DetachError && clearError(info.host) {
return actionUpdate{actionContinue{provResult.RequeueAfter}}
}
return actionContinue{provResult.RequeueAfter}
}
slowPoll := actionContinue{unmanagedRetryDelay}
if info.host.Status.ErrorType == metal3v1alpha1.DetachError {
clearError(info.host)
info.host.Status.ErrorCount = 0
}
if info.host.SetOperationalStatus(metal3v1alpha1.OperationalStatusDetached) {
info.log.Info("host is detached, removed from provisioner")
return actionUpdate{slowPoll}
}
return slowPoll
}

// Test the credentials by connecting to the management controller.
func (r *BareMetalHostReconciler) registerHost(prov provisioner.Provisioner, info *reconcileInfo) actionResult {
info.log.Info("registering and validating access to management controller",
Expand Down
50 changes: 49 additions & 1 deletion controllers/metal3.io/host_state_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ func (hsm *hostStateMachine) ReconcileState(info *reconcileInfo) (actionRes acti
return actionComplete{}
}

if detachedResult := hsm.checkDetachedHost(info); detachedResult != nil {
return detachedResult
}

if registerResult := hsm.ensureRegistered(info); registerResult != nil {
hostRegistrationRequired.Inc()
return registerResult
Expand Down Expand Up @@ -216,7 +220,11 @@ func (hsm *hostStateMachine) checkInitiateDelete() bool {
default:
hsm.NextState = metal3v1alpha1.StateDeleting
case metal3v1alpha1.StateProvisioning, metal3v1alpha1.StateProvisioned:
hsm.NextState = metal3v1alpha1.StateDeprovisioning
if hsm.Host.OperationalStatus() == metal3v1alpha1.OperationalStatusDetached {
hsm.NextState = metal3v1alpha1.StateDeleting
} else {
hsm.NextState = metal3v1alpha1.StateDeprovisioning
}
case metal3v1alpha1.StateDeprovisioning:
// Allow state machine to run to continue deprovisioning.
return false
Expand All @@ -227,6 +235,46 @@ func (hsm *hostStateMachine) checkInitiateDelete() bool {
return true
}

// hasInspectAnnotation checks for existence of baremetalhost.metal3.io/detached
func hasDetachedAnnotation(host *metal3v1alpha1.BareMetalHost) bool {
annotations := host.GetAnnotations()
if annotations != nil {
if _, ok := annotations[metal3v1alpha1.DetachedAnnotation]; ok {
return true
}
}
return false
}

func (hsm *hostStateMachine) checkDetachedHost(info *reconcileInfo) (result actionResult) {
// If the detached annotation is set we remove any host from the
// provisioner and take no further action
// Note this doesn't change the current state, only the OperationalStatus
Comment thread
hardys marked this conversation as resolved.
if hasDetachedAnnotation(hsm.Host) {
// Only allow detaching hosts in Provisioned/ExternallyProvisioned states
switch info.host.Status.Provisioning.State {
case metal3v1alpha1.StateProvisioned, metal3v1alpha1.StateExternallyProvisioned:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's probably a case to be made for allowing this in the Ready/Available state, but I'm fine with leaving it until someone actually has a requirement for it.

return hsm.Reconciler.detachHost(hsm.Provisioner, info)
}
}
if info.host.Status.ErrorType == metal3v1alpha1.DetachError {
Comment thread
hardys marked this conversation as resolved.
clearError(info.host)
hsm.Host.Status.ErrorCount = 0
info.log.Info("removed detach error")
return actionUpdate{}
}
if info.host.OperationalStatus() == metal3v1alpha1.OperationalStatusDetached {
newStatus := metal3v1alpha1.OperationalStatusOK
if info.host.Status.ErrorType != "" {
Comment thread
zaneb marked this conversation as resolved.
newStatus = metal3v1alpha1.OperationalStatusError
}
info.host.SetOperationalStatus(newStatus)
info.log.Info("removed detached status")
return actionUpdate{}
}
return nil
}

func (hsm *hostStateMachine) ensureRegistered(info *reconcileInfo) (result actionResult) {
if !hsm.haveCreds {
// If we are in the process of deletion (which may start with
Expand Down
Loading