diff --git a/pkg/controller/baremetalhost/host_state_machine.go b/pkg/controller/baremetalhost/host_state_machine.go index 065071930e..9364d8cd1a 100644 --- a/pkg/controller/baremetalhost/host_state_machine.go +++ b/pkg/controller/baremetalhost/host_state_machine.go @@ -206,6 +206,11 @@ func (hsm *hostStateMachine) handleRegistrationError(info *reconcileInfo) action hsm.NextState = metal3v1alpha1.StateRegistering return actionComplete{} } + if hsm.Host.Spec.Image != nil { + info.log.Info("Image is set; will retry registration") + hsm.NextState = metal3v1alpha1.StateRegistering + return actionComplete{} + } return actionFailed{} } diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go index a15e38ae0b..f640cb134f 100644 --- a/pkg/provisioner/ironic/ironic.go +++ b/pkg/provisioner/ironic/ironic.go @@ -398,11 +398,6 @@ func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged bool) (r } } - // ironicNode, err = nodes.Get(p.client, p.status.ID).Extract() - // if err != nil { - // return result, errors.Wrap(err, "failed to get provisioning state in ironic") - // } - p.log.Info("current provision state", "lastError", ironicNode.LastError, "current", ironicNode.ProvisionState, @@ -594,15 +589,7 @@ func (p *ironicProvisioner) UpdateHardwareState() (result provisioner.Result, er return result, nil } -func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node) (updates nodes.UpdateOpts, err error) { - - hwProf, err := hardware.GetProfile(p.host.HardwareProfile()) - - if err != nil { - return updates, errors.Wrap(err, - fmt.Sprintf("Could not start provisioning with bad hardware profile %s", - p.host.HardwareProfile())) - } +func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node, forProvisioning bool) (updates nodes.UpdateOpts, err error) { // image_source var op nodes.UpdateOp @@ -622,7 +609,10 @@ func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node) (update }, ) - checksum, checksumType, _ := p.host.GetImageChecksum() + checksum, checksumType, imageOK := p.host.GetImageChecksum() + if !imageOK { + return updates, fmt.Errorf("missing or invalid image details") + } // image_os_hash_algo if _, ok := ironicNode.InstanceInfo["image_os_hash_algo"]; !ok { @@ -703,6 +693,18 @@ func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node) (update }, ) + if !forProvisioning { + return updates, nil + } + + hwProf, err := hardware.GetProfile(p.host.HardwareProfile()) + + if err != nil { + return updates, errors.Wrap(err, + fmt.Sprintf("Could not start provisioning with bad hardware profile %s", + p.host.HardwareProfile())) + } + // root_gb // // FIXME(dhellmann): We have to provide something for the disk @@ -791,8 +793,7 @@ func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node) (update ) // boot_mode - _, ok := ironicNode.InstanceInfo["deploy_boot_mode"] - if !ok { + if _, ok := ironicNode.InstanceInfo["deploy_boot_mode"]; !ok { op = nodes.AddOp } else { op = nodes.ReplaceOp @@ -818,7 +819,7 @@ func (p *ironicProvisioner) startProvisioning(ironicNode *nodes.Node, hostConf p p.log.Info("starting provisioning", "node properties", ironicNode.Properties) - updates, err := p.getUpdateOptsForNode(ironicNode) + updates, err := p.getUpdateOptsForNode(ironicNode, true) _, err = nodes.Update(p.client, ironicNode.UUID, updates).Extract() switch err.(type) { case nil: @@ -892,6 +893,31 @@ func (p *ironicProvisioner) Adopt() (result provisioner.Result, err error) { err = fmt.Errorf("Invalid state for adopt: %s", ironicNode.ProvisionState) case nodes.Manageable: + if p.host.Spec.Image == nil { + // Without an image, adoption will fail. We can stop and + // wait for the image to be set, but we need to do that in + // a way that ensures we don't enter the states where we + // try to manage the host power status. Setting our own + // error message here avoids exposing the error message + // from Ironic that talks about fields in Ironic with + // names the user may not recognize. + result.ErrorMessage = "Image details missing for externally provisioned server." + return + } + + updates, err := p.getUpdateOptsForNode(ironicNode, false) + _, err = nodes.Update(p.client, ironicNode.UUID, updates).Extract() + switch err.(type) { + case nil: + case gophercloud.ErrDefault409: + p.log.Info("could not update host settings in ironic while adopting, busy") + result.Dirty = true + return result, nil + default: + return result, errors.Wrap(err, + "failed to update host settings in ironic while adopting") + } + return p.changeNodeProvisionState( ironicNode, nodes.ProvisionStateOpts{ diff --git a/pkg/provisioner/ironic/ironic_test.go b/pkg/provisioner/ironic/ironic_test.go index 19e19ccd77..0a7dec940a 100644 --- a/pkg/provisioner/ironic/ironic_test.go +++ b/pkg/provisioner/ironic/ironic_test.go @@ -136,7 +136,7 @@ func TestGetUpdateOptsForNodeWithRootHints(t *testing.T) { prov, err := newProvisioner(makeHost(), bmc.Credentials{}, eventPublisher) ironicNode := &nodes.Node{} - patches, err := prov.getUpdateOptsForNode(ironicNode) + patches, err := prov.getUpdateOptsForNode(ironicNode, true) if err != nil { t.Fatal(err) } @@ -219,7 +219,7 @@ func TestGetUpdateOptsForNodeVirtual(t *testing.T) { prov, err := newProvisioner(host, bmc.Credentials{}, eventPublisher) ironicNode := &nodes.Node{} - patches, err := prov.getUpdateOptsForNode(ironicNode) + patches, err := prov.getUpdateOptsForNode(ironicNode, true) if err != nil { t.Fatal(err) } @@ -314,7 +314,7 @@ func TestGetUpdateOptsForNodeDell(t *testing.T) { prov, err := newProvisioner(host, bmc.Credentials{}, eventPublisher) ironicNode := &nodes.Node{} - patches, err := prov.getUpdateOptsForNode(ironicNode) + patches, err := prov.getUpdateOptsForNode(ironicNode, true) if err != nil { t.Fatal(err) } @@ -468,7 +468,7 @@ func TestGetUpdateOptsForNodeBootMode(t *testing.T) { } prov, err := newProvisioner(&host, bmc.Credentials{}, eventPublisher) - patches, err := prov.getUpdateOptsForNode(&tc.Node) + patches, err := prov.getUpdateOptsForNode(&tc.Node, true) if err != nil { t.Fatal(err) } @@ -506,7 +506,8 @@ func makeHost() *metal3v1alpha1.BareMetalHost { }, Spec: metal3v1alpha1.BareMetalHostSpec{ Image: &metal3v1alpha1.Image{ - URL: "not-empty", + URL: "not-empty", + Checksum: "not-empty", }, Online: true, HardwareProfile: "libvirt",