Skip to content

Commit

Permalink
Add support for optional provisioning networks in a Baremetal platform
Browse files Browse the repository at this point in the history
In addition to supporting fully managed provisioning networks with the
option of having external DHCP servers, baremetal installations should
also be able to support cases where a dedicated provisioning network
does not exist.
Details regarding the updates to the provisioning CR to accomodate the
above feature can be found in [1].
Also, removing support for the ConfigMap as way to pass provisioning
configuration to the MAO for baremetal installs.

[1] - https://github.com/openshift/enhancements/blob/master/enhancements/baremetal/baremetal-provisioning-optional.md
  • Loading branch information
sadasu committed Jul 7, 2020
1 parent 168f501 commit 33bb2d6
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,25 @@ spec:
type: string
provisioningOSDownloadURL:
description: provisioningOSDownloadURL is the location from which the OS Image used to boot
baremetal host machines can be downloaded by the metal3 cluster.
baremetal host machines can be downloaded by the metal3 cluster.
type: string
provisioningNetwork:
description: provisioningNetwork provides a way to indicate the state of the underlying
network configuration for the provisioning network. This field can have one of the
following values -
`managed`- when the provisioning network is completely managed by the Baremetal IPI
solution.
`unmanaged`- when the provsioning network is present and used but the user is
responsible for managing DHCP. Virtual media provisioning is recommended but PXE
is still available if required.
`disabled`- when the provisioning network is fully disabled. User can bring up the
baremetal cluster using virtual media or assisted installation. If using metal3 for
power management, BMCs must be accessible from the machine networks. User should
provide two IPs on the external network that would be used for provisioning services.
enum:
- managed
- unmanaged
- disabled
type: string
status:
description: status holds observed values from the cluster. They may not
Expand Down Expand Up @@ -145,8 +163,8 @@ spec:
at the desired state
type: integer
format: int32
version: v1alpha1
version: v1beta1
versions:
- name: v1alpha1
- name: v1beta1
served: true
storage: true
62 changes: 54 additions & 8 deletions pkg/operator/baremetal_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net"

"github.com/golang/glog"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -22,6 +23,9 @@ const (
baremetalKernelUrlSubPath = "images/ironic-python-agent.kernel"
baremetalRamdiskUrlSubPath = "images/ironic-python-agent.initramfs"
baremetalIronicEndpointSubpath = "v1/"
provisioningNetworkManaged = "managed"
provisioningNetworkUnmanaged = "unmanaged"
provisioningNetworkDisabled = "disabled"
)

// Provisioning Config needed to deploy Metal3 pod
Expand All @@ -32,11 +36,16 @@ type BaremetalProvisioningConfig struct {
ProvisioningDHCPExternal bool
ProvisioningDHCPRange string
ProvisioningOSDownloadURL string
ProvisioningNetwork string
}

func getBaremetalProvisioningConfig(dc dynamic.Interface, configName string) (BaremetalProvisioningConfig, error) {
provisioningClient := dc.Resource(provisioningGVR)
provisioningConfig, err := provisioningClient.Get(context.Background(), configName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
glog.V(3).Infof("Baremetal provisioning CR %s is not found", configName)
return BaremetalProvisioningConfig{}, nil
}
if err != nil {
glog.Errorf("Error getting config from Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
Expand All @@ -56,14 +65,13 @@ func getBaremetalProvisioningConfig(dc dynamic.Interface, configName string) (Ba
glog.Errorf("provisioningIP not found in Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}
provisioningNetworkCIDR, found, err := unstructured.NestedString(provisioningSpec, "provisioningNetworkCIDR")
if !found || err != nil {
glog.Errorf("provisioningNetworkCIDR not found in Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
provisioningDHCPExternal, foundDHCP, err := unstructured.NestedBool(provisioningSpec, "provisioningDHCPExternal")
if !foundDHCP {
glog.Infof("provisioningDHCPExternal not found in Baremetal provisioning CR %s", configName)
provisioningDHCPExternal = false
}
provisioningDHCPExternal, found, err := unstructured.NestedBool(provisioningSpec, "provisioningDHCPExternal")
if !found || err != nil {
glog.Errorf("provisioningDHCPExternal not found in Baremetal provisioning CR %s", configName)
if err != nil {
glog.Errorf("Error reading provisioningDHCPExternal from the Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}
provisioningDHCPRange, found, err := unstructured.NestedString(provisioningSpec, "provisioningDHCPRange")
Expand All @@ -76,6 +84,43 @@ func getBaremetalProvisioningConfig(dc dynamic.Interface, configName string) (Ba
glog.Errorf("provisioningOSDownloadURL not found in Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}
// If provisioningNetwork is not provided, set it to a default of "managed".
provisioningNetwork, foundNetwork, err := unstructured.NestedString(provisioningSpec, "provisioningNetwork")
if !foundNetwork {
glog.Infof("provisioningNetwork not found in Baremetal provisioning CR %s", configName)
if !provisioningDHCPExternal {
provisioningNetwork = provisioningNetworkManaged
} else if provisioningDHCPExternal {
provisioningNetwork = provisioningNetworkUnmanaged
}
}
if err != nil {
glog.Errorf("provisioningNetwork not found in Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}
provisioningNetworkCIDR, foundCIDR, err := unstructured.NestedString(provisioningSpec, "provisioningNetworkCIDR")
if !foundCIDR {
glog.Infof("provisioningNetworkCIDR not found in Baremetal provisioning CR %s", configName)
if provisioningNetwork == provisioningNetworkDisabled {
provisioningNetworkCIDR = ""
}
}
// If provisioningNetwork is anything other than Disabled and provisioningNetworkCIDR is not found in the CR,
// treat that as an error.
if (!foundCIDR && provisioningNetwork != provisioningNetworkDisabled) || err != nil {
glog.Errorf("provisioningNetworkCIDR not found in Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}

// Checking for interdependecies within the config
if !foundDHCP && !foundNetwork {
glog.Errorf("Both the provisioningNetwork and provisioningDHCPExternal are not found in baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}
if !foundCIDR && provisioningNetwork != provisioningNetworkDisabled {
glog.Errorf("ProvisioningNewtorkCIDR is expected in the Baremetal provisioning CR %s", configName)
return BaremetalProvisioningConfig{}, err
}

return BaremetalProvisioningConfig{
ProvisioningInterface: provisioningInterface,
Expand All @@ -84,6 +129,7 @@ func getBaremetalProvisioningConfig(dc dynamic.Interface, configName string) (Ba
ProvisioningDHCPExternal: provisioningDHCPExternal,
ProvisioningDHCPRange: provisioningDHCPRange,
ProvisioningOSDownloadURL: provisioningOSDownloadURL,
ProvisioningNetwork: provisioningNetwork,
}, nil
}

Expand Down Expand Up @@ -136,7 +182,7 @@ func getProvisioningDHCPRange(baremetalConfig BaremetalProvisioningConfig) *stri
// to be empty.
if baremetalConfig.ProvisioningDHCPRange != "" {
return &(baremetalConfig.ProvisioningDHCPRange)
} else if baremetalConfig.ProvisioningDHCPExternal {
} else if baremetalConfig.ProvisioningDHCPExternal || baremetalConfig.ProvisioningNetwork != provisioningNetworkManaged {
return &(baremetalConfig.ProvisioningDHCPRange)
}
return nil
Expand Down
20 changes: 4 additions & 16 deletions pkg/operator/baremetal_pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,6 @@ var volumeMounts = []corev1.VolumeMount{
},
}

func buildEnvVarFromConfigMap(name string, key string) corev1.EnvVar {
return corev1.EnvVar{
Name: name,
ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: baremetalConfigmap,
},
Key: key,
},
},
}
}

func buildEnvVar(name string, key string, baremetalProvisioningConfig BaremetalProvisioningConfig) corev1.EnvVar {
value := getMetal3DeploymentConfig(name, baremetalProvisioningConfig)
if value != nil {
Expand All @@ -59,7 +45,7 @@ func buildEnvVar(name string, key string, baremetalProvisioningConfig BaremetalP
Value: *value,
}
} else {
return buildEnvVarFromConfigMap(name, key)
return corev1.EnvVar{}
}
}

Expand Down Expand Up @@ -286,7 +272,9 @@ func newMetal3Containers(config *OperatorConfig, baremetalProvisioningConfig Bar
},
},
}
containers = append(containers, createContainerMetal3Dnsmasq(config, baremetalProvisioningConfig))
if baremetalProvisioningConfig.ProvisioningNetwork != provisioningNetworkDisabled {
containers = append(containers, createContainerMetal3Dnsmasq(config, baremetalProvisioningConfig))
}
containers = append(containers, createContainerMetal3Mariadb(config))
containers = append(containers, createContainerMetal3Httpd(config, baremetalProvisioningConfig))
containers = append(containers, createContainerMetal3IronicConductor(config, baremetalProvisioningConfig))
Expand Down
96 changes: 96 additions & 0 deletions pkg/operator/baremetal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package operator
import (
"testing"

. "github.com/onsi/gomega"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -40,6 +41,50 @@ var (
expectedHttpPort = "6180"
)

var (
managed = `
apiVersion: metal3.io/v1alpha1
kind: Provisioning
metadata:
name: test
spec:
provisioningInterface: "ensp0"
provisioningIP: "172.30.20.3"
provisioningNetworkCIDR: "172.30.20.0/24"
provisioningDHCPExternal: false
provisioningDHCPRange: "172.30.20.11, 172.30.20.101"
provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"
provisioningNetwork: "managed"
`
unmanaged = `
apiVersion: metal3.io/v1alpha1
kind: Provisioning
metadata:
name: test
spec:
provisioningInterface: "ensp0"
provisioningIP: "172.30.20.3"
provisioningNetworkCIDR: "172.30.20.0/24"
provisioningDHCPRange: ""
provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"
provisioningNetwork: "unmanaged"
`
disabled = `
apiVersion: metal3.io/v1alpha1
kind: Provisioning
metadata:
name: test
spec:
provisioningInterface: "ensp0"
provisioningIP: "172.30.20.3"
provisioningNetworkCIDR: "172.30.20.0/24"
provisioningDHCPExternal: false
provisioningDHCPRange: "172.30.20.11, 172.30.20.101"
provisioningOSDownloadURL: "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"
provisioningNetwork: "disabled"
`
)

func TestGenerateRandomPassword(t *testing.T) {
pwd := generateRandomPassword()
if pwd == "" {
Expand Down Expand Up @@ -242,3 +287,54 @@ func TestGetMetal3DeploymentConfig(t *testing.T) {
t.Errorf("Provisioning DHCP Range is not available.")
}
}

// Test interpretation of different provisioning config
func TestBaremetalProvisionigConfig(t *testing.T) {
testConfigResource := "test"
configNames := []string{"PROVISIONING_IP", "PROVISIONING_INTERFACE", "DEPLOY_KERNEL_URL", "DEPLOY_RAMDISK_URL", "IRONIC_ENDPOINT", "IRONIC_INSPECTOR_ENDPOINT", "HTTP_PORT", "DHCP_RANGE", "RHCOS_IMAGE_URL"}

testCases := []struct {
name string
config string
expectedConfigValues []string
}{
{
name: "managed",
config: managed,
expectedConfigValues: []string{"172.30.20.3/24", "ensp0", "http://172.30.20.3:6180/images/ironic-python-agent.kernel", "http://172.30.20.3:6180/images/ironic-python-agent.initramfs", "http://172.30.20.3:6385/v1/", "http://172.30.20.3:5050/v1/", expectedHttpPort, "172.30.20.11, 172.30.20.101", "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"},
},
{
name: "unmanaged",
config: unmanaged,
expectedConfigValues: []string{"172.30.20.3/24", "ensp0", "http://172.30.20.3:6180/images/ironic-python-agent.kernel", "http://172.30.20.3:6180/images/ironic-python-agent.initramfs", "http://172.30.20.3:6385/v1/", "http://172.30.20.3:5050/v1/", expectedHttpPort, "", "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"},
},
{
name: "disabled",
config: disabled,
expectedConfigValues: []string{"172.30.20.3/24", "ensp0", "http://172.30.20.3:6180/images/ironic-python-agent.kernel", "http://172.30.20.3:6180/images/ironic-python-agent.initramfs", "http://172.30.20.3:6385/v1/", "http://172.30.20.3:5050/v1/", expectedHttpPort, "172.30.20.11, 172.30.20.101", "http://172.22.0.1/images/rhcos-44.81.202001171431.0-openstack.x86_64.qcow2.gz?sha256=e98f83a2b9d4043719664a2be75fe8134dc6ca1fdbde807996622f8cc7ecd234"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)

t.Logf("Testing tc : %s", tc.name)
u := &unstructured.Unstructured{Object: map[string]interface{}{}}
if err := yaml.Unmarshal([]byte(tc.config), &u); err != nil {
t.Errorf("failed to unmarshall input yaml content:%v", err)
}
dynamicClient := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), u)
baremetalConfig, err := getBaremetalProvisioningConfig(dynamicClient, testConfigResource)
if err != nil {
t.Errorf("getBaremetalProvisioningConfig returned err: %v", err)
}
g.Expect(err).To(BeNil())

for i, envVar := range configNames {
actualConfigValue := getMetal3DeploymentConfig(envVar, baremetalConfig)

g.Expect(*actualConfigValue).To(Equal(tc.expectedConfigValues[i]))
}
})
}
}

0 comments on commit 33bb2d6

Please sign in to comment.