Repository: metal3-io/baremetal-operator
Pull request #1080 (2022-02-04 17:24:47)

Only update the HFS status on changes
Only update the HFS status if the settings from Ironic have changed or if the conditions were updated.
Currently the reconciler can run an additional time because the Status is updated on each pass.

metal3-io-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **fmuyassarov** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @fmuyassarov` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=metal3-io%2Fbaremetal-operator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/metal3-io/baremetal-operator/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["fmuyassarov"]} -->
zaneb: I suspect that after this change, reconcile will still run again once after an update (to stop that, you have to filter out status changes, see #1079 for details of how the host controller does it). Prior to this it's probably running constantly, limited only by the rate limit in the controller-runtime.
bfournie: > I suspect that after this change, reconcile will still run again once after an update (to stop that, you have to filter out status changes, see #1079 for details of how the host controller does it). Prior to this it's probably running constantly, limited only by the rate limit in the controller-runtime.

Yes, it looks like right now when there is a change to settings (in Spec or Status) I'm seeing a 2nd reconcile run if the Conditions are updated. I should be able to filter that using an eventHandler similar to (bmh https://github.com/metal3-io/baremetal-operator/blob/main/controllers/metal3.io/baremetalhost_controller.go#L1588).

Previously the Status would get updated on each RequeueDelay (every 5 minutes) followed by a 2nd reconcile run immediately after but no more than that. I assume that all the data in the resource was the same (even the condition LastTtansitionTime) so the reconciler would not be invoked.

bfournie: > > I suspect that after this change, reconcile will still run again once after an update (to stop that, you have to filter out status changes, see #1079 for details of how the host controller does it). Prior to this it's probably running constantly, limited only by the rate limit in the controller-runtime.
> 
> Yes, it looks like right now when there is a change to settings (in Spec or Status) I'm seeing a 2nd reconcile run if the Conditions are updated. I should be able to filter that using an eventHandler similar to (bmh https://github.com/metal3-io/baremetal-operator/blob/main/controllers/metal3.io/baremetalhost_controller.go#L1588).
> 
> Previously the Status would get updated on each RequeueDelay (every 5 minutes) followed by a 2nd reconcile run immediately after but no more than that. I assume that all the data in the resource was the same (even the condition LastTtansitionTime) so the reconciler would not be invoked.

I've added an eventHandler to check Generation, this works well to filter out changes the status e.g. Condition updates, that should not cause the reconciler to run again. PTAL.
bfournie: /test-v1b1-ubuntu-integration
dtantsur: /approve
metal3-io-bot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/metal3-io/baremetal-operator/pull/1080#issuecomment-1059232596" title="Approved">dtantsur</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=metal3-io%2Fbaremetal-operator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/metal3-io/baremetal-operator/blob/main/OWNERS)~~ [dtantsur]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
bfournie: /test-v1b1-centos-integration
/test-v1b1-ubuntu-integration

Patch:
diff --git a/Makefile b/Makefile
index 6ee85693..b993fa26 100644
--- a/Makefile
+++ b/Makefile
@@ -50,9 +50,7 @@ help:  ## Display this help
 	@echo "  DEBUG            -- debug flag, if any ($(DEBUG))"
 
 # Image URL to use all building/pushing image targets
-IMG_NAME ?= baremetal-operator
-IMG_TAG ?= latest
-IMG ?= $(IMG_NAME):$(IMG_TAG)
+IMG ?= baremetal-operator:latest
 
 ## --------------------------------------
 ## Test Targets
@@ -131,8 +129,8 @@ uninstall: $(KUSTOMIZE) manifests ## Uninstall CRDs from a cluster
 	$< build config/crd | kubectl delete -f -
 
 .PHONY: deploy
-deploy: $(KUSTOMIZE) manifests  ## Deploy controller in the configured Kubernetes cluster in ~/.kube/config
-	make set-manifest-image-bmo MANIFEST_IMG=$(IMG_NAME) MANIFEST_TAG=$(IMG_TAG)
+deploy: $(KUSTOMIZE) manifests ## Deploy controller in the configured Kubernetes cluster in ~/.kube/config
+	cd config/manager && kustomize edit set image controller=${IMG}
 	$< build config/default | kubectl apply -f -
 
 $(CONTROLLER_GEN): hack/tools/go.mod
@@ -153,31 +151,6 @@ manifests-generate: $(CONTROLLER_GEN)
 manifests-kustomize: $(KUSTOMIZE)
 	$< build config/default > config/render/capm3.yaml
 
-.PHONY: set-manifest-image-bmo
-set-manifest-image-bmo: $(KUSTOMIZE) manifests
-	$(info Updating container image for BMO to use ${MANIFEST_IMG}:${MANIFEST_TAG})
-	cd config/default && $(abspath $(KUSTOMIZE)) edit set image quay.io/metal3-io/baremetal-operator=${MANIFEST_IMG}:${MANIFEST_TAG}
-
-.PHONY: set-manifest-image-ironic
-set-manifest-image-ironic: $(KUSTOMIZE) manifests
-	$(info Updating container image for Ironic to use ${MANIFEST_IMG}:${MANIFEST_TAG})
-	cd ironic-deployment/default && $(abspath $(KUSTOMIZE)) edit set image quay.io/metal3-io/ironic=${MANIFEST_IMG}:${MANIFEST_TAG}
-
-.PHONY: set-manifest-image-mariadb
-set-manifest-image-mariadb: $(KUSTOMIZE) manifests
-	$(info Updating container image for Mariadb to use ${MANIFEST_IMG}:${MANIFEST_TAG})
-	cd ironic-deployment/default && $(abspath $(KUSTOMIZE)) edit set image quay.io/metal3-io/mariadb=${MANIFEST_IMG}:${MANIFEST_TAG}
-
-.PHONY: set-manifest-image-keepalived
-set-manifest-image-keepalived: $(KUSTOMIZE) manifests
-	$(info Updating container image for keepalived to use ${MANIFEST_IMG}:${MANIFEST_TAG})
-	cd ironic-deployment/keepalived && $(abspath $(KUSTOMIZE)) edit set image quay.io/metal3-io/keepalived=${MANIFEST_IMG}:${MANIFEST_TAG}
-
-.PHONY: set-manifest-image-ipa-downloader
-set-manifest-image-ipa-downloader: $(KUSTOMIZE) manifests
-	$(info Updating container image for IPA downloader to use ${MANIFEST_IMG}:${MANIFEST_TAG})
-	cd ironic-deployment/default && $(abspath $(KUSTOMIZE)) edit set image quay.io/metal3-io/ironic-ipa-downloader=${MANIFEST_IMG}:${MANIFEST_TAG}
-
 .PHONY: generate
 generate: $(CONTROLLER_GEN) ## Generate code
 	cd apis; $(abspath $<) object:headerFile="../hack/boilerplate.go.txt" paths="./..."
diff --git a/README.md b/README.md
index 383b3adb..67e9c191 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # Bare Metal Operator
 
-[![Ubuntu V1beta1 build status](https://jenkins.nordix.org/view/Metal3/job/metal3_main_v1b1_integration_test_ubuntu/badge/icon?subject=Ubuntu%20E2E%20V1beta1)](https://jenkins.nordix.org/view/Metal3/job/metal3_main_v1b1_integration_test_ubuntu/)
-[![CentOS V1beta1 build status](https://jenkins.nordix.org/view/Metal3/job/metal3_main_v1b1_integration_test_centos/badge/icon?subject=CentOS%20E2E%20V1beta1)](https://jenkins.nordix.org/view/Metal3/job/metal3_main_v1b1_integration_test_centos/)
+[![Ubuntu V1alpha5 build status](https://jenkins.nordix.org/view/Airship/job/airship_master_v1a5_integration_test_ubuntu/badge/icon?subject=Ubuntu%20E2E%20V1alpha5)](https://jenkins.nordix.org/view/Airship/job/airship_master_v1a5_integration_test_ubuntu/)
+[![CentOS V1alpha5 build status](https://jenkins.nordix.org/view/Airship/job/airship_master_v1a5_integration_test_centos/badge/icon?subject=CentOS%20E2E%20V1alpha5)](https://jenkins.nordix.org/view/Airship/job/airship_master_v1a5_integration_test_centos/)
 
 The Bare Metal Operator implements a Kubernetes API for managing bare metal
 hosts. It maintains an inventory of available hosts as instances of the
diff --git a/apis/metal3.io/v1alpha1/baremetalhost_validation.go b/apis/metal3.io/v1alpha1/baremetalhost_validation.go
index f2b91efc..dbaf2f52 100644
--- a/apis/metal3.io/v1alpha1/baremetalhost_validation.go
+++ b/apis/metal3.io/v1alpha1/baremetalhost_validation.go
@@ -2,7 +2,6 @@ package v1alpha1
 
 import (
 	"fmt"
-	"net"
 	"regexp"
 
 	"github.com/google/uuid"
@@ -85,13 +84,6 @@ func validateBMCAccess(s BareMetalHostSpec, bmcAccess bmc.AccessDetails) []error
 		errs = append(errs, fmt.Errorf("BMC driver %s requires a BootMACAddress value", bmcAccess.Type()))
 	}
 
-	if s.BootMACAddress != "" {
-		_, err := net.ParseMAC(s.BootMACAddress)
-		if err != nil {
-			errs = append(errs, err)
-		}
-	}
-
 	if s.BootMode == UEFISecureBoot && !bmcAccess.SupportsSecureBoot() {
 		errs = append(errs, fmt.Errorf("BMC driver %s does not support secure boot", bmcAccess.Type()))
 	}
diff --git a/controllers/metal3.io/baremetalhost_controller.go b/controllers/metal3.io/baremetalhost_controller.go
index 7de67bde..9d5eed0e 100644
--- a/controllers/metal3.io/baremetalhost_controller.go
+++ b/controllers/metal3.io/baremetalhost_controller.go
@@ -663,12 +663,12 @@ func (r *BareMetalHostReconciler) preprovImageAvailable(info *reconcileInfo, ima
 		return false, nil
 	}
 
-	if errCond := meta.FindStatusCondition(image.Status.Conditions, string(metal3v1alpha1.ConditionImageError)); errCond != nil && errCond.Status == metav1.ConditionTrue {
+	if errCond := meta.FindStatusCondition(image.Status.Conditions, string(metal3v1alpha1.ConditionImageError)); errCond.Status == metav1.ConditionTrue {
 		info.log.Info("error building PreprovisioningImage",
 			"message", errCond.Message)
 		return false, imageBuildError{errCond.Message}
 	}
-	if readyCond := meta.FindStatusCondition(image.Status.Conditions, string(metal3v1alpha1.ConditionImageReady)); readyCond != nil && readyCond.Status == metav1.ConditionTrue && readyCond.ObservedGeneration == image.Generation {
+	if meta.IsStatusConditionTrue(image.Status.Conditions, string(metal3v1alpha1.ConditionImageReady)) {
 		return true, nil
 	}
 
@@ -715,7 +715,6 @@ func (r *BareMetalHostReconciler) getPreprovImage(info *reconcileInfo, formats [
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      key.Name,
 				Namespace: key.Namespace,
-				Labels:    info.host.Labels,
 			},
 			Spec: expectedSpec,
 		}
@@ -727,27 +726,12 @@ func (r *BareMetalHostReconciler) getPreprovImage(info *reconcileInfo, formats [
 		return nil, errors.Wrap(err, "failed to retrieve pre-provisioning image data")
 	}
 
-	needsUpdate := false
-	if preprovImage.Labels == nil && len(info.host.Labels) > 0 {
-		preprovImage.Labels = make(map[string]string, len(info.host.Labels))
-	}
-	for k, v := range info.host.Labels {
-		if cur, ok := preprovImage.Labels[k]; !ok || cur != v {
-			preprovImage.Labels[k] = v
-			needsUpdate = true
-		}
-	}
 	if !apiequality.Semantic.DeepEqual(preprovImage.Spec, expectedSpec) {
 		info.log.Info("updating PreprovisioningImage spec")
 		preprovImage.Spec = expectedSpec
-		needsUpdate = true
-	}
-	if needsUpdate {
-		info.log.Info("updating PreprovisioningImage")
 		err = r.Update(context.TODO(), &preprovImage)
 		return nil, err
 	}
-
 	if available, err := r.preprovImageAvailable(info, &preprovImage); err != nil || !available {
 		return nil, err
 	}
@@ -967,7 +951,7 @@ func (r *BareMetalHostReconciler) actionMatchProfile(prov provisioner.Provisione
 func (r *BareMetalHostReconciler) actionPreparing(prov provisioner.Provisioner, info *reconcileInfo) actionResult {
 	info.log.Info("preparing")
 
-	bmhDirty, newStatus, err := getHostProvisioningSettings(info.host, info)
+	bmhDirty, newStatus, err := getHostProvisioningSettings(info.host)
 	if err != nil {
 		return actionError{err}
 	}
@@ -1016,7 +1000,7 @@ func (r *BareMetalHostReconciler) actionPreparing(prov provisioner.Provisioner,
 
 	if bmhDirty && started {
 		info.log.Info("saving host provisioning settings")
-		_, err := saveHostProvisioningSettings(info.host, info)
+		_, err := saveHostProvisioningSettings(info.host)
 		if err != nil {
 			return actionError{errors.Wrap(err, "could not save the host provisioning settings")}
 		}
@@ -1312,9 +1296,9 @@ func (r *BareMetalHostReconciler) actionManageAvailable(prov provisioner.Provisi
 	return r.manageHostPower(prov, info)
 }
 
-func getHostProvisioningSettings(host *metal3v1alpha1.BareMetalHost, info *reconcileInfo) (dirty bool, status *metal3v1alpha1.BareMetalHostStatus, err error) {
+func getHostProvisioningSettings(host *metal3v1alpha1.BareMetalHost) (dirty bool, status *metal3v1alpha1.BareMetalHostStatus, err error) {
 	hostCopy := host.DeepCopy()
-	dirty, err = saveHostProvisioningSettings(hostCopy, info)
+	dirty, err = saveHostProvisioningSettings(hostCopy)
 	if err != nil {
 		err = errors.Wrap(err, "could not determine the host provisioning settings")
 	}
@@ -1325,23 +1309,22 @@ func getHostProvisioningSettings(host *metal3v1alpha1.BareMetalHost, info *recon
 // saveHostProvisioningSettings copies the values related to
 // provisioning that do not trigger re-provisioning into the status
 // fields of the host.
-func saveHostProvisioningSettings(host *metal3v1alpha1.BareMetalHost, info *reconcileInfo) (dirty bool, err error) {
+func saveHostProvisioningSettings(host *metal3v1alpha1.BareMetalHost) (dirty bool, err error) {
 
 	// Ensure the root device hints we're going to use are stored.
 	//
 	// If the user has provided explicit root device hints, they take
 	// precedence. Otherwise use the values from the hardware profile.
-	hintSource := host.Spec.RootDeviceHints.DeepCopy()
+	hintSource := host.Spec.RootDeviceHints
 	if hintSource == nil {
 		hwProf, err := hardware.GetProfile(host.HardwareProfile())
 		if err != nil {
 			return false, errors.Wrap(err, "Could not update root device hints")
 		}
-		hintSource = hwProf.RootDeviceHints.DeepCopy()
+		hintSource = &hwProf.RootDeviceHints
 	}
-	if !reflect.DeepEqual(hintSource, host.Status.Provisioning.RootDeviceHints) {
-		host.Status.Provisioning.RootDeviceHints = hintSource.DeepCopy()
-		info.log.Info("RootDeviceHints have changed")
+	if (hintSource != nil && host.Status.Provisioning.RootDeviceHints == nil) || *hintSource != *(host.Status.Provisioning.RootDeviceHints) {
+		host.Status.Provisioning.RootDeviceHints = hintSource
 		dirty = true
 	}
 
@@ -1363,14 +1346,12 @@ func saveHostProvisioningSettings(host *metal3v1alpha1.BareMetalHost, info *reco
 	}
 	if !reflect.DeepEqual(host.Status.Provisioning.RAID, specRAID) {
 		host.Status.Provisioning.RAID = specRAID
-		info.log.Info("RAID settings have changed")
 		dirty = true
 	}
 
 	// Copy BIOS settings
 	if !reflect.DeepEqual(host.Status.Provisioning.Firmware, host.Spec.Firmware) {
 		host.Status.Provisioning.Firmware = host.Spec.Firmware
-		info.log.Info("Firmware settings have changed")
 		dirty = true
 	}
 
diff --git a/controllers/metal3.io/host_state_machine.go b/controllers/metal3.io/host_state_machine.go
index 7e79b297..3ead90de 100644
--- a/controllers/metal3.io/host_state_machine.go
+++ b/controllers/metal3.io/host_state_machine.go
@@ -412,7 +412,7 @@ func (hsm *hostStateMachine) handleAvailable(info *reconcileInfo) actionResult {
 		return actionComplete{}
 	}
 
-	if dirty, _, err := getHostProvisioningSettings(info.host, info); err != nil {
+	if dirty, _, err := getHostProvisioningSettings(info.host); err != nil {
 		return actionError{err}
 	} else if dirty {
 		hsm.NextState = metal3v1alpha1.StatePreparing
diff --git a/controllers/metal3.io/hostfirmwaresettings_controller.go b/controllers/metal3.io/hostfirmwaresettings_controller.go
index 60355366..090536c8 100644
--- a/controllers/metal3.io/hostfirmwaresettings_controller.go
+++ b/controllers/metal3.io/hostfirmwaresettings_controller.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"crypto/sha256"
 	"fmt"
+	"reflect"
 	"sort"
 	"strconv"
 	"strings"
@@ -35,6 +36,8 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
 
 	metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
 	"github.com/metal3-io/baremetal-operator/pkg/provisioner"
@@ -179,11 +182,6 @@ func (r *HostFirmwareSettingsReconciler) updateHostFirmwareSettings(currentSetti
 		return errors.Wrap(err, "could not get/create firmware schema")
 	}
 
-	// Set hostFirmwareSetting to use this schema
-	info.hfs.Status.FirmwareSchema = &metal3v1alpha1.SchemaReference{
-		Namespace: firmwareSchema.ObjectMeta.Namespace,
-		Name:      firmwareSchema.ObjectMeta.Name}
-
 	if err = r.updateStatus(info, currentSettings, firmwareSchema); err != nil {
 		return errors.Wrap(err, "could not update hostFirmwareSettings")
 	}
@@ -194,9 +192,14 @@ func (r *HostFirmwareSettingsReconciler) updateHostFirmwareSettings(currentSetti
 // Update the HostFirmwareSettings resource using the settings and schema from provisioner
 func (r *HostFirmwareSettingsReconciler) updateStatus(info *rInfo, settings metal3v1alpha1.SettingsMap, schema *metal3v1alpha1.FirmwareSchema) (err error) {
 
-	if info.hfs.Status.Settings == nil {
-		info.hfs.Status.Settings = make(metal3v1alpha1.SettingsMap)
-	}
+	dirty := false
+	var newStatus metal3v1alpha1.HostFirmwareSettingsStatus
+	newStatus.Settings = make(metal3v1alpha1.SettingsMap)
+
+	// Set hostFirmwareSetting to use this schema
+	newStatus.FirmwareSchema = &metal3v1alpha1.SchemaReference{
+		Namespace: schema.ObjectMeta.Namespace,
+		Name:      schema.ObjectMeta.Name}
 
 	// Update Status on changes
 	for k, v := range settings {
@@ -205,14 +208,18 @@ func (r *HostFirmwareSettingsReconciler) updateStatus(info *rInfo, settings meta
 			continue
 		}
 
-		info.hfs.Status.Settings[k] = v
+		newStatus.Settings[k] = v
 	}
 
+	dirty = !reflect.DeepEqual(info.hfs.Status.FirmwareSchema, newStatus.FirmwareSchema) ||
+		!reflect.DeepEqual(info.hfs.Status.Settings, newStatus.Settings)
+
 	// Check if any Spec settings are different than Status
 	specMismatch := false
 	for k, v := range info.hfs.Spec.Settings {
-		if statusVal, ok := info.hfs.Status.Settings[k]; ok {
+		if statusVal, ok := newStatus.Settings[k]; ok {
 			if v.String() != statusVal {
+				info.log.Info("spec value different than status", "name", k, "specvalue", v.String(), "statusvalue", statusVal)
 				specMismatch = true
 				break
 			}
@@ -228,34 +235,46 @@ func (r *HostFirmwareSettingsReconciler) updateStatus(info *rInfo, settings meta
 	generation := info.hfs.GetGeneration()
 
 	if specMismatch {
-		setCondition(generation, &info.hfs.Status, metal3v1alpha1.FirmwareSettingsChangeDetected, metav1.ConditionTrue, reason, "")
+		if setCondition(generation, &newStatus, info, metal3v1alpha1.FirmwareSettingsChangeDetected, metav1.ConditionTrue, reason, "") {
+			dirty = true
+		}
 
 		// Run validation on the Spec to detect invalid values entered by user, including Spec settings not in Status
 		// Eventually this will be handled by a webhook
-		errors := r.validateHostFirmwareSettings(info, schema)
+		errors := r.validateHostFirmwareSettings(info, &newStatus, schema)
 		if len(errors) == 0 {
-			setCondition(generation, &info.hfs.Status, metal3v1alpha1.FirmwareSettingsValid, metav1.ConditionTrue, reason, "")
+			if setCondition(generation, &newStatus, info, metal3v1alpha1.FirmwareSettingsValid, metav1.ConditionTrue, reason, "") {
+				dirty = true
+			}
 		} else {
 			for _, error := range errors {
 				info.publishEvent("ValidationFailed", fmt.Sprintf("Invalid BIOS setting: %v", error))
 			}
-
 			reason = reasonConfigurationError
-			setCondition(generation, &info.hfs.Status, metal3v1alpha1.FirmwareSettingsValid, metav1.ConditionFalse, reason, "Invalid BIOS setting")
+			if setCondition(generation, &newStatus, info, metal3v1alpha1.FirmwareSettingsValid, metav1.ConditionFalse, reason, "Invalid BIOS setting") {
+				dirty = true
+			}
 		}
 	} else {
-		// Reset conditions
-		if meta.IsStatusConditionTrue(info.hfs.Status.Conditions, string(metal3v1alpha1.FirmwareSettingsChangeDetected)) {
-			setCondition(generation, &info.hfs.Status, metal3v1alpha1.FirmwareSettingsChangeDetected, metav1.ConditionFalse, reason, "")
+		if setCondition(generation, &newStatus, info, metal3v1alpha1.FirmwareSettingsValid, metav1.ConditionTrue, reason, "") {
+			dirty = true
 		}
-		if !meta.IsStatusConditionTrue(info.hfs.Status.Conditions, string(metal3v1alpha1.FirmwareSettingsValid)) {
-			setCondition(generation, &info.hfs.Status, metal3v1alpha1.FirmwareSettingsValid, metav1.ConditionTrue, reason, "")
+		if setCondition(generation, &newStatus, info, metal3v1alpha1.FirmwareSettingsChangeDetected, metav1.ConditionFalse, reason, "") {
+			dirty = true
 		}
 	}
 
-	t := metav1.Now()
-	info.hfs.Status.LastUpdated = &t
-	return r.Status().Update(context.TODO(), info.hfs)
+	// Update Status if it has changed
+	if dirty {
+		info.log.Info("Status has changed")
+		info.hfs.Status = *newStatus.DeepCopy()
+
+		t := metav1.Now()
+		info.hfs.Status.LastUpdated = &t
+		return r.Status().Update(context.TODO(), info.hfs)
+	}
+	return nil
+
 }
 
 // Get a firmware schema that matches the host vendor or create one if it doesn't exist
@@ -332,11 +351,28 @@ func (r *HostFirmwareSettingsReconciler) SetupWithManager(mgr ctrl.Manager) erro
 
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&metal3v1alpha1.HostFirmwareSettings{}).
+		WithEventFilter(
+			predicate.Funcs{
+				UpdateFunc: r.updateEventHandler,
+			}).
 		Complete(r)
 }
 
+func (r *HostFirmwareSettingsReconciler) updateEventHandler(e event.UpdateEvent) bool {
+
+	r.Log.Info("hostfirmwaresettings in event handler")
+
+	//If the update increased the resource Generation then let's process it
+	if e.ObjectNew.GetGeneration() != e.ObjectOld.GetGeneration() {
+		r.Log.Info("returning true as generation changed from event handler")
+		return true
+	}
+
+	return false
+}
+
 // Validate the HostFirmwareSetting Spec against the schema
-func (r *HostFirmwareSettingsReconciler) validateHostFirmwareSettings(info *rInfo, schema *metal3v1alpha1.FirmwareSchema) []error {
+func (r *HostFirmwareSettingsReconciler) validateHostFirmwareSettings(info *rInfo, status *metal3v1alpha1.HostFirmwareSettingsStatus, schema *metal3v1alpha1.FirmwareSchema) []error {
 
 	var errors []error
 
@@ -348,7 +384,7 @@ func (r *HostFirmwareSettingsReconciler) validateHostFirmwareSettings(info *rInf
 		}
 
 		// The setting must be in the Status
-		if _, ok := info.hfs.Status.Settings[name]; !ok {
+		if _, ok := status.Settings[name]; !ok {
 			errors = append(errors, fmt.Errorf("Setting %s is not in the Status field", name))
 			continue
 		}
@@ -379,9 +415,9 @@ func (r *HostFirmwareSettingsReconciler) publishEvent(request ctrl.Request, even
 	return
 }
 
-func setCondition(generation int64, status *metal3v1alpha1.HostFirmwareSettingsStatus,
+func setCondition(generation int64, status *metal3v1alpha1.HostFirmwareSettingsStatus, info *rInfo,
 	cond metal3v1alpha1.SettingsConditionType, newStatus metav1.ConditionStatus,
-	reason conditionReason, message string) {
+	reason conditionReason, message string) bool {
 	newCondition := metav1.Condition{
 		Type:               string(cond),
 		Status:             newStatus,
@@ -390,6 +426,12 @@ func setCondition(generation int64, status *metal3v1alpha1.HostFirmwareSettingsS
 		Message:            message,
 	}
 	meta.SetStatusCondition(&status.Conditions, newCondition)
+
+	currCond := meta.FindStatusCondition(info.hfs.Status.Conditions, string(cond))
+	if currCond == nil || currCond.Status != newStatus {
+		return true
+	}
+	return false
 }
 
 // Generate a name based on the schema key and values which should be the same for similar hardware
diff --git a/docs/deploying.md b/docs/deploying.md
index 74b1654b..62b6efec 100644
--- a/docs/deploying.md
+++ b/docs/deploying.md
@@ -107,6 +107,7 @@ ironic-deployment/
 │   │   └── kustomization.yaml
 │   ├── ironic-auth-config-tpl
 │   ├── ironic-inspector-auth-config-tpl
+│   ├── ironic-rpc-auth-config-tpl
 │   ├── keepalived
 │   │   ├── auth.yaml
 │   │   └── kustomization.yaml
diff --git a/ironic-deployment/basic-auth/default/auth.yaml b/ironic-deployment/basic-auth/default/auth.yaml
index 83cfc90e..98550cff 100644
--- a/ironic-deployment/basic-auth/default/auth.yaml
+++ b/ironic-deployment/basic-auth/default/auth.yaml
@@ -6,19 +6,27 @@ spec:
   template:
     spec:
       containers:
-      - name: ironic
+      - name: ironic-api
         volumeMounts:
-        - name: ironic-auth-config
-          mountPath: "/auth/ironic"
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
           readOnly: true
+        envFrom:
+        - configMapRef:
+            name: ironic-htpasswd
+        - configMapRef:
+            name: ironic-bmo-configmap
+      - name: ironic-conductor
+        volumeMounts:
         - name: ironic-inspector-auth-config
           mountPath: "/auth/ironic-inspector"
           readOnly: true
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
+          readOnly: true
         envFrom:
         - configMapRef:
             name: ironic-htpasswd
-        - configMapRef:
-            name: ironic-inspector-htpasswd
         - configMapRef:
             name: ironic-bmo-configmap
       - name: ironic-inspector
@@ -26,12 +34,7 @@ spec:
         - name: ironic-auth-config
           mountPath: "/auth/ironic"
           readOnly: true
-        - name: ironic-inspector-auth-config
-          mountPath: "/auth/ironic-inspector"
-          readOnly: true
         envFrom:
-        - configMapRef:
-            name: ironic-htpasswd
         - configMapRef:
             name: ironic-inspector-htpasswd
         - configMapRef:
@@ -43,3 +46,6 @@ spec:
       - name: ironic-inspector-auth-config
         secret:
           secretName: ironic-inspector-auth-config
+      - name: ironic-rpc-auth-config
+        secret:
+          secretName: ironic-rpc-auth-config
\ No newline at end of file
diff --git a/ironic-deployment/basic-auth/default/kustomization.yaml b/ironic-deployment/basic-auth/default/kustomization.yaml
index 93681e7b..1e6cceba 100644
--- a/ironic-deployment/basic-auth/default/kustomization.yaml
+++ b/ironic-deployment/basic-auth/default/kustomization.yaml
@@ -21,6 +21,9 @@ secretGenerator:
 - name: ironic-inspector-auth-config
   files:
   - auth-config=ironic-inspector-auth-config
+- name: ironic-rpc-auth-config
+  files:
+  - auth-config=ironic-rpc-auth-config
 
 patchesStrategicMerge:
 - auth.yaml
diff --git a/ironic-deployment/basic-auth/keepalived/auth.yaml b/ironic-deployment/basic-auth/keepalived/auth.yaml
index 83cfc90e..98550cff 100644
--- a/ironic-deployment/basic-auth/keepalived/auth.yaml
+++ b/ironic-deployment/basic-auth/keepalived/auth.yaml
@@ -6,19 +6,27 @@ spec:
   template:
     spec:
       containers:
-      - name: ironic
+      - name: ironic-api
         volumeMounts:
-        - name: ironic-auth-config
-          mountPath: "/auth/ironic"
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
           readOnly: true
+        envFrom:
+        - configMapRef:
+            name: ironic-htpasswd
+        - configMapRef:
+            name: ironic-bmo-configmap
+      - name: ironic-conductor
+        volumeMounts:
         - name: ironic-inspector-auth-config
           mountPath: "/auth/ironic-inspector"
           readOnly: true
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
+          readOnly: true
         envFrom:
         - configMapRef:
             name: ironic-htpasswd
-        - configMapRef:
-            name: ironic-inspector-htpasswd
         - configMapRef:
             name: ironic-bmo-configmap
       - name: ironic-inspector
@@ -26,12 +34,7 @@ spec:
         - name: ironic-auth-config
           mountPath: "/auth/ironic"
           readOnly: true
-        - name: ironic-inspector-auth-config
-          mountPath: "/auth/ironic-inspector"
-          readOnly: true
         envFrom:
-        - configMapRef:
-            name: ironic-htpasswd
         - configMapRef:
             name: ironic-inspector-htpasswd
         - configMapRef:
@@ -43,3 +46,6 @@ spec:
       - name: ironic-inspector-auth-config
         secret:
           secretName: ironic-inspector-auth-config
+      - name: ironic-rpc-auth-config
+        secret:
+          secretName: ironic-rpc-auth-config
\ No newline at end of file
diff --git a/ironic-deployment/basic-auth/keepalived/kustomization.yaml b/ironic-deployment/basic-auth/keepalived/kustomization.yaml
index 24793713..34061bde 100644
--- a/ironic-deployment/basic-auth/keepalived/kustomization.yaml
+++ b/ironic-deployment/basic-auth/keepalived/kustomization.yaml
@@ -21,6 +21,9 @@ secretGenerator:
 - name: ironic-inspector-auth-config
   files:
   - auth-config=ironic-inspector-auth-config
+- name: ironic-rpc-auth-config
+  files:
+  - auth-config=ironic-rpc-auth-config
 
 patchesStrategicMerge:
-- auth.yaml
+- auth.yaml
\ No newline at end of file
diff --git a/ironic-deployment/basic-auth/tls/default/auth.yaml b/ironic-deployment/basic-auth/tls/default/auth.yaml
index a5d288f6..98550cff 100644
--- a/ironic-deployment/basic-auth/tls/default/auth.yaml
+++ b/ironic-deployment/basic-auth/tls/default/auth.yaml
@@ -6,29 +6,27 @@ spec:
   template:
     spec:
       containers:
-      - name: ironic
+      - name: ironic-api
         volumeMounts:
-        - name: ironic-inspector-auth-config
-          mountPath: "/auth/ironic-inspector"
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
           readOnly: true
         envFrom:
         - configMapRef:
             name: ironic-htpasswd
         - configMapRef:
             name: ironic-bmo-configmap
-      - name: ironic-httpd
+      - name: ironic-conductor
         volumeMounts:
-        - name: ironic-auth-config
-          mountPath: "/auth/ironic"
-          readOnly: true
         - name: ironic-inspector-auth-config
           mountPath: "/auth/ironic-inspector"
           readOnly: true
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
+          readOnly: true
         envFrom:
         - configMapRef:
             name: ironic-htpasswd
-        - configMapRef:
-            name: ironic-inspector-htpasswd
         - configMapRef:
             name: ironic-bmo-configmap
       - name: ironic-inspector
@@ -48,3 +46,6 @@ spec:
       - name: ironic-inspector-auth-config
         secret:
           secretName: ironic-inspector-auth-config
+      - name: ironic-rpc-auth-config
+        secret:
+          secretName: ironic-rpc-auth-config
\ No newline at end of file
diff --git a/ironic-deployment/basic-auth/tls/default/kustomization.yaml b/ironic-deployment/basic-auth/tls/default/kustomization.yaml
index 4462dc36..a750b9ac 100644
--- a/ironic-deployment/basic-auth/tls/default/kustomization.yaml
+++ b/ironic-deployment/basic-auth/tls/default/kustomization.yaml
@@ -21,6 +21,9 @@ secretGenerator:
 - name: ironic-inspector-auth-config
   files:
   - auth-config=ironic-inspector-auth-config
+- name: ironic-rpc-auth-config
+  files:
+  - auth-config=ironic-rpc-auth-config
 
 patchesStrategicMerge:
 - auth.yaml
diff --git a/ironic-deployment/basic-auth/tls/keepalived/auth.yaml b/ironic-deployment/basic-auth/tls/keepalived/auth.yaml
index a5d288f6..98550cff 100644
--- a/ironic-deployment/basic-auth/tls/keepalived/auth.yaml
+++ b/ironic-deployment/basic-auth/tls/keepalived/auth.yaml
@@ -6,29 +6,27 @@ spec:
   template:
     spec:
       containers:
-      - name: ironic
+      - name: ironic-api
         volumeMounts:
-        - name: ironic-inspector-auth-config
-          mountPath: "/auth/ironic-inspector"
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
           readOnly: true
         envFrom:
         - configMapRef:
             name: ironic-htpasswd
         - configMapRef:
             name: ironic-bmo-configmap
-      - name: ironic-httpd
+      - name: ironic-conductor
         volumeMounts:
-        - name: ironic-auth-config
-          mountPath: "/auth/ironic"
-          readOnly: true
         - name: ironic-inspector-auth-config
           mountPath: "/auth/ironic-inspector"
           readOnly: true
+        - name: ironic-rpc-auth-config
+          mountPath: "/auth/ironic-rpc"
+          readOnly: true
         envFrom:
         - configMapRef:
             name: ironic-htpasswd
-        - configMapRef:
-            name: ironic-inspector-htpasswd
         - configMapRef:
             name: ironic-bmo-configmap
       - name: ironic-inspector
@@ -48,3 +46,6 @@ spec:
       - name: ironic-inspector-auth-config
         secret:
           secretName: ironic-inspector-auth-config
+      - name: ironic-rpc-auth-config
+        secret:
+          secretName: ironic-rpc-auth-config
\ No newline at end of file
diff --git a/ironic-deployment/basic-auth/tls/keepalived/kustomization.yaml b/ironic-deployment/basic-auth/tls/keepalived/kustomization.yaml
index 12ab9607..fa1188c9 100644
--- a/ironic-deployment/basic-auth/tls/keepalived/kustomization.yaml
+++ b/ironic-deployment/basic-auth/tls/keepalived/kustomization.yaml
@@ -21,6 +21,9 @@ secretGenerator:
 - name: ironic-inspector-auth-config
   files:
   - auth-config=ironic-inspector-auth-config
+- name: ironic-rpc-auth-config
+  files:
+  - auth-config=ironic-rpc-auth-config
 
 patchesStrategicMerge:
 - auth.yaml
diff --git a/ironic-deployment/default/ironic_bmo_configmap.env b/ironic-deployment/default/ironic_bmo_configmap.env
index fee40ba5..c2a03d2b 100644
--- a/ironic-deployment/default/ironic_bmo_configmap.env
+++ b/ironic-deployment/default/ironic_bmo_configmap.env
@@ -9,5 +9,3 @@ CACHEURL=http://172.22.0.1/images
 IRONIC_FAST_TRACK=true
 IRONIC_KERNEL_PARAMS=console=ttyS0
 IRONIC_INSPECTOR_VLAN_INTERFACES=all
-# TODO(dtantsur): this should probably not be the default
-IRONIC_USE_MARIADB=true
diff --git a/ironic-deployment/ironic/ironic.yaml b/ironic-deployment/ironic/ironic.yaml
index 19de2863..e27cfbf7 100644
--- a/ironic-deployment/ironic/ironic.yaml
+++ b/ironic-deployment/ironic/ironic.yaml
@@ -81,11 +81,11 @@ spec:
                  configMapKeyRef:
                   name: ironic-bmo-configmap
                   key: RESTART_CONTAINER_CERTIFICATE_UPDATED
-        - name: ironic
+        - name: ironic-api
           image: quay.io/metal3-io/ironic
           imagePullPolicy: Always
           command:
-            - /bin/runironic
+            - /bin/runironic-api
           livenessProbe:
            exec:
              command: ["sh", "-c", "curl -sSf http://127.0.0.1:6385 || curl -sSfk https://127.0.0.1:6385"]
@@ -114,6 +114,39 @@ spec:
                 secretKeyRef:
                   name: mariadb-password
                   key: password
+        - name: ironic-conductor
+          image: quay.io/metal3-io/ironic
+          imagePullPolicy: Always
+          command:
+            - /bin/runironic-conductor
+          readinessProbe:
+            exec:
+              command: ["sh", "-c", "curl -sd '{}' -o – -k https://127.0.0.1:8089 || curl -sd '{}' -o – http://127.0.0.1:8089"]
+            initialDelaySeconds: 30
+            periodSeconds: 30
+            timeoutSeconds: 10
+            successThreshold: 1
+            failureThreshold: 10
+          livenessProbe:
+            exec:
+              command: ["sh", "-c", "curl -sd '{}' -o – -k https://127.0.0.1:8089 || curl -sd '{}' -o – http://127.0.0.1:8089"]
+            initialDelaySeconds: 30
+            periodSeconds: 30
+            timeoutSeconds: 10
+            successThreshold: 1
+            failureThreshold: 10
+          volumeMounts:
+            - mountPath: /shared
+              name: ironic-data-volume
+          envFrom:
+            - configMapRef:
+                name: ironic-bmo-configmap
+          env:
+            - name: MARIADB_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  name: mariadb-password
+                  key: password
         - name: ironic-log-watch
           image: quay.io/metal3-io/ironic
           imagePullPolicy: Always
@@ -146,33 +179,6 @@ spec:
           envFrom:
             - configMapRef:
                 name: ironic-bmo-configmap
-        - name: ironic-httpd
-          image: quay.io/metal3-io/ironic
-          imagePullPolicy: Always
-          command:
-            - /bin/runhttpd
-          livenessProbe:
-           exec:
-             command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6180"]
-           initialDelaySeconds: 30
-           periodSeconds: 30
-           timeoutSeconds: 10
-           successThreshold: 1
-           failureThreshold: 10
-          readinessProbe:
-           exec:
-             command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6180"]
-           initialDelaySeconds: 30
-           periodSeconds: 30
-           timeoutSeconds: 10
-           successThreshold: 1
-           failureThreshold: 10
-          volumeMounts:
-            - mountPath: /shared
-              name: ironic-data-volume
-          envFrom:
-            - configMapRef:
-                name: ironic-bmo-configmap
       initContainers:
         - name: ironic-ipa-downloader
           image: quay.io/metal3-io/ironic-ipa-downloader
diff --git a/ironic-deployment/keepalived/ironic_bmo_configmap.env b/ironic-deployment/keepalived/ironic_bmo_configmap.env
index ca2ac486..57f49c33 100644
--- a/ironic-deployment/keepalived/ironic_bmo_configmap.env
+++ b/ironic-deployment/keepalived/ironic_bmo_configmap.env
@@ -9,5 +9,3 @@ IRONIC_INSPECTOR_ENDPOINT=http://172.22.0.2:5050/v1/
 CACHEURL=http://172.22.0.1/images
 IRONIC_FAST_TRACK=true
 IRONIC_KERNEL_PARAMS=console=ttyS0
-# TODO(dtantsur): this should probably not be the default
-IRONIC_USE_MARIADB=true
diff --git a/ironic-deployment/tls/default/tls.yaml b/ironic-deployment/tls/default/tls.yaml
index 5412b20b..4a0bf9c1 100644
--- a/ironic-deployment/tls/default/tls.yaml
+++ b/ironic-deployment/tls/default/tls.yaml
@@ -6,71 +6,44 @@ spec:
   template:
     spec:
       containers:
-      - name: ironic
-        readinessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSf http://127.0.0.1:6388"]
-        livenessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSf http://127.0.0.1:6388"]
-        env:
-        - name: IRONIC_REVERSE_PROXY_SETUP
-          value: "true"
-        - name: INSPECTOR_REVERSE_PROXY_SETUP
-          value: "true"
+      - name: ironic-api
         volumeMounts:
+        - name: cert-ironic
+          mountPath: "/certs/ironic"
+          readOnly: true
         - name: cert-ironic-ca
           mountPath: "/certs/ca/ironic"
           readOnly: true
         - name: cert-ironic-inspector-ca
           mountPath: "/certs/ca/ironic-inspector"
           readOnly: true
+        - name: cert-ironic-inspector
+          mountPath: "/certs/ironic-inspector"
+          readOnly: true
         - name: cert-mariadb-ca
           mountPath: "/certs/ca/mariadb"
           readOnly: true
-      - name: ironic-httpd
-        livenessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"]
-        readinessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"]
-        env:
-        - name: IRONIC_REVERSE_PROXY_SETUP
-          value: "true"
-        - name: INSPECTOR_REVERSE_PROXY_SETUP
-          value: "true"
+      - name: ironic-conductor
         volumeMounts:
         - name: cert-ironic
           mountPath: "/certs/ironic"
           readOnly: true
-        - name: cert-ironic-inspector
-          mountPath: "/certs/ironic-inspector"
-          readOnly: true
         - name: cert-ironic-ca
           mountPath: "/certs/ca/ironic"
           readOnly: true
         - name: cert-ironic-inspector-ca
           mountPath: "/certs/ca/ironic-inspector"
           readOnly: true
+        - name: cert-mariadb-ca
+          mountPath: "/certs/ca/mariadb"
+          readOnly: true
       - name: ironic-inspector
-        readinessProbe:
-          exec:
-            command: ["sh", "-c", "curl -sSf http://127.0.0.1:5049"]
-        livenessProbe:
-          exec:
-            command: ["sh", "-c", "curl -sSf http://127.0.0.1:5049"]
-        env:
-        - name: IRONIC_REVERSE_PROXY_SETUP
-          value: "true"
-        - name: INSPECTOR_REVERSE_PROXY_SETUP
-          value: "true"
         volumeMounts:
         - name: cert-ironic-ca
           mountPath: "/certs/ca/ironic"
           readOnly: true
-        - name: cert-ironic-inspector-ca
-          mountPath: "/certs/ca/ironic-inspector"
+        - name: cert-ironic-inspector
+          mountPath: "/certs/ironic-inspector"
           readOnly: true
       - name: mariadb
         volumeMounts:
@@ -98,4 +71,4 @@ spec:
           secretName: mariadb-cert
       - name: cert-mariadb-ca
         secret:
-          secretName: ironic-cacert
+          secretName: ironic-cacert
\ No newline at end of file
diff --git a/ironic-deployment/tls/keepalived/tls.yaml b/ironic-deployment/tls/keepalived/tls.yaml
index 5412b20b..818d3436 100644
--- a/ironic-deployment/tls/keepalived/tls.yaml
+++ b/ironic-deployment/tls/keepalived/tls.yaml
@@ -6,71 +6,44 @@ spec:
   template:
     spec:
       containers:
-      - name: ironic
-        readinessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSf http://127.0.0.1:6388"]
-        livenessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSf http://127.0.0.1:6388"]
-        env:
-        - name: IRONIC_REVERSE_PROXY_SETUP
-          value: "true"
-        - name: INSPECTOR_REVERSE_PROXY_SETUP
-          value: "true"
+      - name: ironic-api
         volumeMounts:
+        - name: cert-ironic
+          mountPath: "/certs/ironic"
+          readOnly: true
         - name: cert-ironic-ca
           mountPath: "/certs/ca/ironic"
           readOnly: true
         - name: cert-ironic-inspector-ca
           mountPath: "/certs/ca/ironic-inspector"
           readOnly: true
+        - name: cert-ironic-inspector
+          mountPath: "/certs/ironic-inspector"
+          readOnly: true
         - name: cert-mariadb-ca
           mountPath: "/certs/ca/mariadb"
           readOnly: true
-      - name: ironic-httpd
-        livenessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"]
-        readinessProbe:
-         exec:
-           command: ["sh", "-c", "curl -sSfk https://127.0.0.1:6385"]
-        env:
-        - name: IRONIC_REVERSE_PROXY_SETUP
-          value: "true"
-        - name: INSPECTOR_REVERSE_PROXY_SETUP
-          value: "true"
+      - name: ironic-conductor
         volumeMounts:
         - name: cert-ironic
           mountPath: "/certs/ironic"
           readOnly: true
-        - name: cert-ironic-inspector
-          mountPath: "/certs/ironic-inspector"
-          readOnly: true
         - name: cert-ironic-ca
           mountPath: "/certs/ca/ironic"
           readOnly: true
         - name: cert-ironic-inspector-ca
           mountPath: "/certs/ca/ironic-inspector"
           readOnly: true
+        - name: cert-mariadb-ca
+          mountPath: "/certs/ca/mariadb"
+          readOnly: true
       - name: ironic-inspector
-        readinessProbe:
-          exec:
-            command: ["sh", "-c", "curl -sSf http://127.0.0.1:5049"]
-        livenessProbe:
-          exec:
-            command: ["sh", "-c", "curl -sSf http://127.0.0.1:5049"]
-        env:
-        - name: IRONIC_REVERSE_PROXY_SETUP
-          value: "true"
-        - name: INSPECTOR_REVERSE_PROXY_SETUP
-          value: "true"
         volumeMounts:
         - name: cert-ironic-ca
           mountPath: "/certs/ca/ironic"
           readOnly: true
-        - name: cert-ironic-inspector-ca
-          mountPath: "/certs/ca/ironic-inspector"
+        - name: cert-ironic-inspector
+          mountPath: "/certs/ironic-inspector"
           readOnly: true
       - name: mariadb
         volumeMounts:
diff --git a/tools/deploy.sh b/tools/deploy.sh
index 2f028cf8..872d89a2 100755
--- a/tools/deploy.sh
+++ b/tools/deploy.sh
@@ -2,88 +2,22 @@
 
 set -eu
 
-function usage {
-    echo "Usage : deploy.sh [-b -i -t -n -k]"
+if [ -z "${1:-}" ] || [ -z "${2:-}" ] || [ -z "${3:-}" ] || [ -z "${4:-}" ] || [ -z "${5:-}" ]; then
+    echo "Usage : deploy.sh <deploy-BMO> <deploy-Ironic> <deploy-TLS> <deploy-Basic-Auth> <deploy-Keepalived>"
     echo ""
-    echo "       -b: deploy BMO"
-    echo "       -i: deploy Ironic"
-    echo "       -t: deploy with TLS enabled"
-    echo "       -n: deploy without authentication"
-    echo "       -k: deploy with keepalived"
-}
-
-DEPLOY_BMO=false
-DEPLOY_IRONIC=false
-DEPLOY_TLS=false
-DEPLOY_BASIC_AUTH=true
-DEPLOY_KEEPALIVED=false
-
-while getopts ":hbitnk" options; do
-    case "${options}" in
-        h)
-            usage
-            exit 0
-            ;;
-        b)
-            DEPLOY_BMO=true
-            ;;
-        i)
-            DEPLOY_IRONIC=true
-            ;;
-        t)
-            DEPLOY_TLS=true
-            ;;
-        n)
-            echo "WARNING: Deploying without authentication is not recommended"
-            DEPLOY_BASIC_AUTH=false
-            ;;
-        k)
-            DEPLOY_KEEPALIVED=true
-            ;;
-        :)
-            echo "ERROR: -${OPTARG} requires an argument"
-            usage
-            exit 1
-            ;;
-        *)
-            usage
-            exit 1
-            ;;
-    esac
-done
-
-# Backward compatibility
-shift $(( OPTIND - 1 ))
-if [ $# -gt 0 ]; then
-    echo "WARNING: positional arguments are deprecated, run deploy.sh -h for information"
-fi
-
-if [ -n "${1:-}" ]; then
-    DEPLOY_BMO=$1
-fi
-
-if [ -n "${2:-}" ]; then
-    DEPLOY_IRONIC=$2
-fi
-
-if [ -n "${3:-}" ]; then
-    DEPLOY_TLS=$3
-fi
-
-if [ -n "${4:-}" ]; then
-    DEPLOY_BASIC_AUTH=$4
-fi
-
-if [ -n "${5:-}" ]; then
-    DEPLOY_KEEPALIVED=$5
-fi
-
-if [ "$DEPLOY_BMO" == "false" ] && [ "$DEPLOY_IRONIC" == "false" ]; then
-    echo "ERROR: nothing to deploy"
-    usage
+    echo "       deploy-BMO: deploy BareMetal Operator : \"true\" or \"false\""
+    echo "       deploy-Ironic: deploy Ironic : \"true\" or \"false\""
+    echo "       deploy-TLS: deploy with TLS enabled : \"true\" or \"false\""
+    echo "       deploy-Basic-Auth: deploy with Basic Auth enabled : \"true\" or \"false\""
+    echo "       deploy-Keepalived: deploy with Keepalived for ironic : \"true\" or \"false\""
     exit 1
 fi
 
+DEPLOY_BMO="${1,,}"
+DEPLOY_IRONIC="${2,,}"
+DEPLOY_TLS="${3,,}"
+DEPLOY_BASIC_AUTH="${4,,}"
+DEPLOY_KEEPALIVED="${5,,}"
 MARIADB_HOST_IP="${MARIADB_HOST_IP:-"127.0.0.1"}"
 KUBECTL_ARGS="${KUBECTL_ARGS:-""}"
 RESTART_CONTAINER_CERTIFICATE_UPDATED=${RESTART_CONTAINER_CERTIFICATE_UPDATED:-"false"}
@@ -194,10 +128,12 @@ if [ "${DEPLOY_BASIC_AUTH}" == "true" ]; then
         "${IRONIC_SCENARIO}/ironic-auth-config"
         envsubst < "${SCRIPTDIR}/ironic-deployment/basic-auth/ironic-inspector-auth-config-tpl" > \
         "${IRONIC_SCENARIO}/ironic-inspector-auth-config"
+        envsubst < "${SCRIPTDIR}/ironic-deployment/basic-auth/ironic-rpc-auth-config-tpl" > \
+        "${IRONIC_SCENARIO}/ironic-rpc-auth-config"
 
-        echo "IRONIC_HTPASSWD=$(htpasswd -n -b -B "${IRONIC_USERNAME}" "${IRONIC_PASSWORD}")" > \
+        echo "HTTP_BASIC_HTPASSWD=$(htpasswd -n -b -B "${IRONIC_USERNAME}" "${IRONIC_PASSWORD}")" > \
         "${IRONIC_SCENARIO}/ironic-htpasswd"
-        echo "INSPECTOR_HTPASSWD=$(htpasswd -n -b -B "${IRONIC_INSPECTOR_USERNAME}" \
+        echo "HTTP_BASIC_HTPASSWD=$(htpasswd -n -b -B "${IRONIC_INSPECTOR_USERNAME}" \
         "${IRONIC_INSPECTOR_PASSWORD}")" > "${IRONIC_SCENARIO}/ironic-inspector-htpasswd"
     fi
 fi
@@ -252,6 +188,7 @@ if [ "${DEPLOY_BASIC_AUTH}" == "true" ]; then
     if [ "${DEPLOY_IRONIC}" == "true" ]; then
         rm "${IRONIC_SCENARIO}/ironic-auth-config"
         rm "${IRONIC_SCENARIO}/ironic-inspector-auth-config"
+        rm "${IRONIC_SCENARIO}/ironic-rpc-auth-config"
 
         rm "${IRONIC_SCENARIO}/ironic-htpasswd"
         rm "${IRONIC_SCENARIO}/ironic-inspector-htpasswd"
diff --git a/tools/run_local_ironic.sh b/tools/run_local_ironic.sh
index d776ada2..2afffe17 100755
--- a/tools/run_local_ironic.sh
+++ b/tools/run_local_ironic.sh
@@ -179,7 +179,10 @@ IRONIC_HTPASSWD=""
 if [ -n "$IRONIC_USERNAME" ]; then
      envsubst < "${SCRIPTDIR}/ironic-deployment/basic-auth/ironic-auth-config-tpl" > \
         "${IRONIC_DATA_DIR}/auth/ironic-auth-config"
+     envsubst < "${SCRIPTDIR}/ironic-deployment/basic-auth/ironic-rpc-auth-config-tpl" > \
+        "${IRONIC_DATA_DIR}/auth/ironic-rpc-auth-config"
      BASIC_AUTH_MOUNTS="-v ${IRONIC_DATA_DIR}/auth/ironic-auth-config:/auth/ironic/auth-config"
+     BASIC_AUTH_MOUNTS="${BASIC_AUTH_MOUNTS} -v ${IRONIC_DATA_DIR}/auth/ironic-rpc-auth-config:/auth/ironic-rpc/auth-config"
      IRONIC_HTPASSWD="$(htpasswd -n -b -B "${IRONIC_USERNAME}" "${IRONIC_PASSWORD}")"
      IRONIC_HTPASSWD="--env HTTP_BASIC_HTPASSWD=${IRONIC_HTPASSWD} --env IRONIC_HTPASSWD=${IRONIC_HTPASSWD}"
 fi


Test patch:
diff --git a/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go b/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go
index 29b6b978..26e4beff 100644
--- a/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go
+++ b/apis/metal3.io/v1alpha1/baremetalhost_validation_test.go
@@ -263,22 +263,6 @@ func TestValidateCreate(t *testing.T) {
 			oldBMH:    nil,
 			wantedErr: "BMC driver libvirt does not support secure boot",
 		},
-		{
-			name: "InvalidBootMACAddress",
-			newBMH: &BareMetalHost{
-				TypeMeta:   tm,
-				ObjectMeta: om,
-				Spec: BareMetalHostSpec{
-					BMC: BMCDetails{
-						Address:         "irmc://127.0.1.1",
-						CredentialsName: "test1",
-					},
-					BootMACAddress: "00:00:00:00:00",
-					BootMode:       UEFISecureBoot,
-				}},
-			oldBMH:    nil,
-			wantedErr: "address 00:00:00:00:00: invalid MAC address",
-		},
 		{
 			name: "UEFISecureBootWithSupportBMC",
 			newBMH: &BareMetalHost{
diff --git a/controllers/metal3.io/baremetalhost_controller_test.go b/controllers/metal3.io/baremetalhost_controller_test.go
index e5659d3b..33e22fc7 100644
--- a/controllers/metal3.io/baremetalhost_controller_test.go
+++ b/controllers/metal3.io/baremetalhost_controller_test.go
@@ -1460,8 +1460,6 @@ func TestDeleteHost(t *testing.T) {
 // precedence rules to the root device hints settings for a host.
 func TestUpdateRootDeviceHints(t *testing.T) {
 	rotational := true
-	rotationalTwo := true
-	rotationalFalse := false
 
 	testCases := []struct {
 		Scenario string
@@ -1491,9 +1489,6 @@ func TestUpdateRootDeviceHints(t *testing.T) {
 						WWNVendorExtension: "userd_vendor_extension",
 						Rotational:         &rotational,
 					},
-					RAID: &metal3v1alpha1.RAIDConfig{
-						SoftwareRAIDVolumes: []metal3v1alpha1.SoftwareRAIDVolume{},
-					},
 				},
 				Status: metal3v1alpha1.BareMetalHostStatus{
 					HardwareProfile: "libvirt",
@@ -1555,87 +1550,18 @@ func TestUpdateRootDeviceHints(t *testing.T) {
 				DeviceName: "/dev/sda",
 			},
 		},
-
-		{
-			Scenario: "rotational values same",
-			Host: metal3v1alpha1.BareMetalHost{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "myhost",
-					Namespace: "myns",
-					UID:       "27720611-e5d1-45d3-ba3a-222dcfaa4ca2",
-				},
-				Spec: metal3v1alpha1.BareMetalHostSpec{
-					HardwareProfile: "libvirt",
-					RootDeviceHints: &metal3v1alpha1.RootDeviceHints{
-						MinSizeGigabytes: 40,
-						Rotational:       &rotational,
-					},
-				},
-				Status: metal3v1alpha1.BareMetalHostStatus{
-					HardwareProfile: "libvirt",
-					Provisioning: metal3v1alpha1.ProvisionStatus{
-						RootDeviceHints: &metal3v1alpha1.RootDeviceHints{
-							MinSizeGigabytes: 40,
-							Rotational:       &rotationalTwo,
-						},
-						RAID: &metal3v1alpha1.RAIDConfig{
-							SoftwareRAIDVolumes: []metal3v1alpha1.SoftwareRAIDVolume{},
-						}},
-				},
-			},
-			Dirty: false,
-			Expected: &metal3v1alpha1.RootDeviceHints{
-				MinSizeGigabytes: 40,
-				Rotational:       &rotational,
-			},
-		},
-
-		{
-			Scenario: "rotational values different",
-			Host: metal3v1alpha1.BareMetalHost{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "myhost",
-					Namespace: "myns",
-					UID:       "27720611-e5d1-45d3-ba3a-222dcfaa4ca2",
-				},
-				Spec: metal3v1alpha1.BareMetalHostSpec{
-					HardwareProfile: "libvirt",
-					RootDeviceHints: &metal3v1alpha1.RootDeviceHints{
-						MinSizeGigabytes: 40,
-						Rotational:       &rotational,
-					},
-				},
-				Status: metal3v1alpha1.BareMetalHostStatus{
-					HardwareProfile: "libvirt",
-					Provisioning: metal3v1alpha1.ProvisionStatus{
-						RootDeviceHints: &metal3v1alpha1.RootDeviceHints{
-							MinSizeGigabytes: 40,
-							Rotational:       &rotationalFalse,
-						},
-						RAID: &metal3v1alpha1.RAIDConfig{
-							SoftwareRAIDVolumes: []metal3v1alpha1.SoftwareRAIDVolume{},
-						}},
-				},
-			},
-			Dirty: true,
-			Expected: &metal3v1alpha1.RootDeviceHints{
-				MinSizeGigabytes: 40,
-				Rotational:       &rotational,
-			},
-		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.Scenario, func(t *testing.T) {
-			info := makeReconcileInfo(&tc.Host)
-			dirty, newStatus, err := getHostProvisioningSettings(&tc.Host, info)
+			dirty, newStatus, err := getHostProvisioningSettings(&tc.Host)
 			if err != nil {
 				t.Fatal(err)
 			}
 			assert.Equal(t, tc.Dirty, dirty, "dirty flag did not match")
 			assert.Equal(t, tc.Expected, newStatus.Provisioning.RootDeviceHints)
 
-			dirty, err = saveHostProvisioningSettings(&tc.Host, info)
+			dirty, err = saveHostProvisioningSettings(&tc.Host)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -2051,11 +1977,10 @@ func TestUpdateRAID(t *testing.T) {
 	for _, c := range cases {
 		t.Run(c.name, func(t *testing.T) {
 			host.Spec.RAID = c.raid
-			info := makeReconcileInfo(&host)
-			dirty, _ := saveHostProvisioningSettings(&host, info)
+			dirty, _ := saveHostProvisioningSettings(&host)
 			assert.Equal(t, c.dirty, dirty)
 			assert.Equal(t, c.expected, host.Status.Provisioning.RAID)
-			dirty, _, _ = getHostProvisioningSettings(&host, info)
+			dirty, _, _ = getHostProvisioningSettings(&host)
 			assert.Equal(t, false, dirty)
 		})
 	}
@@ -2237,9 +2162,6 @@ func TestGetPreprovImageCreateUpdate(t *testing.T) {
 	secretName := "net_secret"
 	host := newDefaultHost(t)
 	host.Spec.PreprovisioningNetworkDataName = secretName
-	host.Labels = map[string]string{
-		"answer.metal3.io": "42",
-	}
 	r := newTestReconciler(host, newSecret(secretName, nil))
 	i := makeReconcileInfo(host)
 
@@ -2255,11 +2177,9 @@ func TestGetPreprovImageCreateUpdate(t *testing.T) {
 		&img))
 	assert.Equal(t, "x86_64", img.Spec.Architecture)
 	assert.Equal(t, secretName, img.Spec.NetworkDataName)
-	assert.Equal(t, "42", img.Labels["answer.metal3.io"])
 
 	newSecretName := "new_net_secret"
 	host.Spec.PreprovisioningNetworkDataName = newSecretName
-	host.Labels["cat.metal3.io"] = "meow"
 
 	imgData, err = r.getPreprovImage(i, []metal3v1alpha1.ImageFormat{"iso"})
 	assert.NoError(t, err)
@@ -2271,8 +2191,6 @@ func TestGetPreprovImageCreateUpdate(t *testing.T) {
 	},
 		&img))
 	assert.Equal(t, newSecretName, img.Spec.NetworkDataName)
-	assert.Equal(t, "42", img.Labels["answer.metal3.io"])
-	assert.Equal(t, "meow", img.Labels["cat.metal3.io"])
 }
 
 func TestGetPreprovImage(t *testing.T) {
diff --git a/controllers/metal3.io/host_state_machine_test.go b/controllers/metal3.io/host_state_machine_test.go
index c65c8267..0b93f10a 100644
--- a/controllers/metal3.io/host_state_machine_test.go
+++ b/controllers/metal3.io/host_state_machine_test.go
@@ -1129,8 +1129,7 @@ func (hb *hostBuilder) build() *metal3v1alpha1.BareMetalHost {
 }
 
 func (hb *hostBuilder) SaveHostProvisioningSettings() *hostBuilder {
-	info := makeDefaultReconcileInfo(&hb.BareMetalHost)
-	saveHostProvisioningSettings(&hb.BareMetalHost, info)
+	saveHostProvisioningSettings(&hb.BareMetalHost)
 	return hb
 }
 
diff --git a/controllers/metal3.io/hostfirmwaresettings_test.go b/controllers/metal3.io/hostfirmwaresettings_test.go
index bd69b4a2..3ecd510f 100644
--- a/controllers/metal3.io/hostfirmwaresettings_test.go
+++ b/controllers/metal3.io/hostfirmwaresettings_test.go
@@ -328,6 +328,7 @@ func TestStoreHostFirmwareSettings(t *testing.T) {
 					},
 					Conditions: []metav1.Condition{
 						{Type: "Valid", Status: "True", Reason: "Success"},
+						{Type: "ChangeDetected", Status: "False", Reason: "Success"},
 					},
 				},
 			},
@@ -367,6 +368,7 @@ func TestStoreHostFirmwareSettings(t *testing.T) {
 					},
 					Conditions: []metav1.Condition{
 						{Type: "Valid", Status: "True", Reason: "Success"},
+						{Type: "ChangeDetected", Status: "False", Reason: "Success"},
 					},
 				},
 			},
@@ -537,6 +539,7 @@ func TestStoreHostFirmwareSettings(t *testing.T) {
 					},
 					Conditions: []metav1.Condition{
 						{Type: "Valid", Status: "True", Reason: "Success"},
+						{Type: "ChangeDetected", Status: "False", Reason: "Success"},
 					},
 				},
 			},
@@ -712,7 +715,7 @@ func TestValidateHostFirmwareSettings(t *testing.T) {
 				hfs: hfs,
 			}
 
-			errors := r.validateHostFirmwareSettings(info, getExpectedSchema())
+			errors := r.validateHostFirmwareSettings(info, &info.hfs.Status, getExpectedSchema())
 			if len(errors) == 0 {
 				assert.Equal(t, tc.ExpectedError, "")
 			} else {