Repository: metal3-io/baremetal-operator
Pull request #2477 (2025-05-22 13:42:53)

✨ Allow disabling drivers that require a provisioning network
It's a common mistake among my customers to use drivers like `ipmi`,
`redfish` (as opposed to e.g. `redfish-virtualmedia`) without configuring
a provisioning network.

This change adds a new environment variable to tell BMO that no
provisioning network is available.

Signed-off-by: Dmitry Tantsur <dtantsur@protonmail.com>



Patch:
diff --git a/docs/configuration.md b/docs/configuration.md
index e6142081..c3e1b63c 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -46,6 +46,10 @@ BMO_CONCURRENCY value lower than the requested PROVISIONING_LIMIT. Default is 20
 image for nodes that use IPv6. In dual stack environments, this can be
 used to tell Ironic which IP version it should set on the BMC.
 
+`PROVISIONING_NETWORK_DISABLED` -- Set to `true` if your deployment does not
+feature a provisioning network. This option disables drivers that require a
+provisioning network (such as IPMI).
+
 Kustomization Configuration
 ---------------------------
 
diff --git a/pkg/provisioner/ironic/factory.go b/pkg/provisioner/ironic/factory.go
index 19571eb0..95cc21b4 100644
--- a/pkg/provisioner/ironic/factory.go
+++ b/pkg/provisioner/ironic/factory.go
@@ -154,6 +154,8 @@ func loadConfigFromEnv(havePreprovImgBuilder bool) (ironicConfig, error) {
 		}
 	}
 
+	c.provNetDisabled = strings.ToLower(os.Getenv("PROVISIONING_NETWORK_DISABLED")) == "true"
+
 	return c, nil
 }
 
diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go
index 38a126d5..61340890 100644
--- a/pkg/provisioner/ironic/ironic.go
+++ b/pkg/provisioner/ironic/ironic.go
@@ -69,6 +69,7 @@ type ironicConfig struct {
 	liveISOForcePersistentBootDevice string
 	maxBusyHosts                     int
 	externalURL                      string
+	provNetDisabled                  bool
 }
 
 // Provisioner implements the provisioning.Provisioner interface
diff --git a/pkg/provisioner/ironic/register.go b/pkg/provisioner/ironic/register.go
index 48e069a0..78f84436 100644
--- a/pkg/provisioner/ironic/register.go
+++ b/pkg/provisioner/ironic/register.go
@@ -55,6 +55,13 @@ func (p *ironicProvisioner) Register(data provisioner.ManagementAccessData, cred
 		return result, "", err
 	}
 
+	if bmcAccess.RequiresProvisioningNetwork() && p.config.provNetDisabled {
+		msg := fmt.Sprintf("BMC driver %s requires a provisioning network", bmcAccess.Type())
+		p.log.Info(msg)
+		result, err = operationFailed(msg)
+		return result, "", err
+	}
+
 	// Refuse to manage a node that has Disabled Power off if not supported by ironic,
 	// accidentally powering it off would require a arctic expedition to the data center
 	if data.DisablePowerOff && !p.availableFeatures.HasDisablePowerOff() {


Test patch:
diff --git a/pkg/provisioner/ironic/register_test.go b/pkg/provisioner/ironic/register_test.go
index e6c302b5..338ae558 100644
--- a/pkg/provisioner/ironic/register_test.go
+++ b/pkg/provisioner/ironic/register_test.go
@@ -846,6 +846,28 @@ func TestRegisterUnsupportedSecureBoot(t *testing.T) {
 	assert.Contains(t, result.ErrorMessage, "does not support secure boot")
 }
 
+func TestRegisterUnsupportedDriverWithoutProvNet(t *testing.T) {
+	host := makeHost()
+	host.Status.Provisioning.ID = "" // so we don't lookup by uuid
+
+	ironic := testserver.NewIronic(t).NoNode("myns" + nameSeparator + host.Name).NoNode(host.Name)
+	ironic.Start()
+	defer ironic.Stop()
+
+	auth := clients.AuthConfig{Type: clients.NoAuth}
+	prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nil, ironic.Endpoint(), auth)
+	if err != nil {
+		t.Fatalf("could not create provisioner: %s", err)
+	}
+	prov.config.provNetDisabled = true
+
+	result, _, err := prov.Register(provisioner.ManagementAccessData{}, false, false)
+	if err != nil {
+		t.Fatalf("error from Register: %s", err)
+	}
+	assert.Contains(t, result.ErrorMessage, "requires a provisioning network")
+}
+
 func TestRegisterNoBMCDetails(t *testing.T) {
 	ironic := testserver.NewIronic(t)
 	ironic.Start()