diff --git a/data/data/baremetal/bootstrap/main.tf b/data/data/baremetal/bootstrap/main.tf index 37e7ceb2d25..a99b9d06cb5 100644 --- a/data/data/baremetal/bootstrap/main.tf +++ b/data/data/baremetal/bootstrap/main.tf @@ -37,12 +37,11 @@ resource "libvirt_domain" "bootstrap" { mode = "host-passthrough" } - network_interface { - bridge = var.external_bridge - } - - network_interface { - bridge = var.provisioning_bridge + dynamic "network_interface" { + for_each = var.bridges + content { + bridge = network_interface.value + } } } diff --git a/data/data/baremetal/bootstrap/variables.tf b/data/data/baremetal/bootstrap/variables.tf index 35ec8da6826..577a8234346 100644 --- a/data/data/baremetal/bootstrap/variables.tf +++ b/data/data/baremetal/bootstrap/variables.tf @@ -13,12 +13,7 @@ variable "ignition" { description = "The content of the bootstrap ignition file." } -variable "external_bridge" { - type = string - description = "The name of the bridge providing external access" -} - -variable "provisioning_bridge" { - type = string - description = "The name of the bridge used for provisioning" +variable "bridges" { + type = list(string) + description = "A list of network interfaces to attach to the bootstrap virtual machine" } diff --git a/data/data/baremetal/main.tf b/data/data/baremetal/main.tf index b355a61bd6f..02a5ada3b1e 100644 --- a/data/data/baremetal/main.tf +++ b/data/data/baremetal/main.tf @@ -12,11 +12,10 @@ provider "ironic" { module "bootstrap" { source = "./bootstrap" - cluster_id = var.cluster_id - image = var.bootstrap_os_image - ignition = var.ignition_bootstrap - external_bridge = var.external_bridge - provisioning_bridge = var.provisioning_bridge + cluster_id = var.cluster_id + image = var.bootstrap_os_image + ignition = var.ignition_bootstrap + bridges = compact([var.external_bridge, var.provisioning_bridge]) } module "masters" { diff --git a/data/data/bootstrap/baremetal/files/usr/local/bin/startironic.sh.template b/data/data/bootstrap/baremetal/files/usr/local/bin/startironic.sh.template index d98e47e33f3..3d40e24602e 100755 --- a/data/data/bootstrap/baremetal/files/usr/local/bin/startironic.sh.template +++ b/data/data/bootstrap/baremetal/files/usr/local/bin/startironic.sh.template @@ -23,10 +23,16 @@ for name in ironic-api ironic-conductor ironic-inspector dnsmasq httpd mariadb i done # Start the provisioning nic if not already started -PROVISIONING_NIC=ens4 -if ! nmcli -t device | grep "$PROVISIONING_NIC:ethernet:connected:provisioning"; then +PROVISIONING_NIC={{.PlatformData.BareMetal.ProvisioningInterface}} + +if ! nmcli -t device | grep "$PROVISIONING_NIC:ethernet:connected"; then nmcli c add type ethernet ifname $PROVISIONING_NIC con-name provisioning {{ if .PlatformData.BareMetal.ProvisioningIPv6 }} ip6 {{ else }} ip4 {{ end }} {{.PlatformData.BareMetal.ProvisioningIP}}/{{.PlatformData.BareMetal.ProvisioningCIDR}} nmcli c up provisioning +else + connection=$(nmcli -t device show $PROVISIONING_NIC | grep GENERAL.CONNECTION | cut -d: -f2) + nmcli con modify "$connection" ifname $PROVISIONING_NIC {{ if .PlatformData.BareMetal.ProvisioningIPv6 }} ip6 {{ else }} ip4 {{ end }} {{.PlatformData.BareMetal.ProvisioningIP}}/{{.PlatformData.BareMetal.ProvisioningCIDR}} + nmcli con reload "$connection" + nmcli con up "$connection" fi # Wait for the interface to come up @@ -53,14 +59,19 @@ done # Start dnsmasq, http, mariadb, and ironic containers using same image # Currently we do this outside of a pod because we need to ensure the images # are downloaded before starting the API pods -podman run -d --net host --privileged --name mariadb \ - -v $IRONIC_SHARED_VOLUME:/shared:z --entrypoint /bin/runmariadb \ - --env MARIADB_PASSWORD=$mariadb_password ${IRONIC_IMAGE} - -podman run -d --net host --privileged --name dnsmasq \ +{{ if .PlatformData.BareMetal.ProvisioningDNSMasq }} +dnsmasq_container_name="dnsmasq" +podman run -d --net host --privileged --name $dnsmasq_container_name \ --env PROVISIONING_INTERFACE=$PROVISIONING_NIC \ --env DHCP_RANGE=$DHCP_RANGE \ -v $IRONIC_SHARED_VOLUME:/shared:z --entrypoint /bin/rundnsmasq ${IRONIC_IMAGE} +{{ else }} +dnsmasq_container_name="" +{{ end }} + +podman run -d --net host --privileged --name mariadb \ + -v $IRONIC_SHARED_VOLUME:/shared:z --entrypoint /bin/runmariadb \ + --env MARIADB_PASSWORD=$mariadb_password ${IRONIC_IMAGE} podman run -d --net host --privileged --name httpd \ --env PROVISIONING_INTERFACE=$PROVISIONING_NIC \ @@ -146,7 +157,7 @@ sudo podman run -d --net host --privileged --name ironic-api \ # Now loop so the service remains active and restart everything should one of the containers exit unexpectedly. # The alternative would be RemainAfterExit=yes but then we lose the ability to restart if something crashes. while true; do - for name in ironic-api ironic-conductor ironic-inspector dnsmasq httpd mariadb; do + for name in ironic-api ironic-conductor ironic-inspector $dnsmasq_container_name httpd mariadb; do # Note there are two levels of go templating here, the outer braces # are for the templating done in openshift-install, to escape the # templating input to the --format option of podman inspect diff --git a/data/data/install.openshift.io_installconfigs.yaml b/data/data/install.openshift.io_installconfigs.yaml index 8db693a5af7..f4c4894f7b5 100644 --- a/data/data/install.openshift.io_installconfigs.yaml +++ b/data/data/install.openshift.io_installconfigs.yaml @@ -1119,11 +1119,9 @@ spec: on the host that will run the bootstrap VM. type: string provisioningDHCPExternal: - description: ProvisioningDHCPExternal indicates that DHCP is provided - by an external service, appropriately configured with next-server - set to BootstrapProvisioningIP for the control plane, and ClusterProvisioningIP - for workers. The default for this field is false, which means - we will start and manage a DHCP server on the provisioning network. + description: DeprecatedProvisioningDHCPExternal indicates that + DHCP is provided by an external service. This parameter is replaced + by ProvisioningNetwork being set to "Unmanaged". type: boolean provisioningDHCPRange: description: ProvisioningDHCPRange is used to provide DHCP services @@ -1135,6 +1133,16 @@ spec: services, and an http server to cache some downloaded content e.g RHCOS/IPA images type: string + provisioningNetwork: + default: Managed + description: ProvisioningNetwork is used to indicate if we will + have a provisioning network, and how it will be managed. + enum: + - "" + - Managed + - Unmanaged + - Disabled + type: string provisioningNetworkCIDR: description: ProvisioningNetworkCIDR defines the network to use for provisioning. diff --git a/data/data/manifests/openshift/baremetal-provisioning-config.yaml.template b/data/data/manifests/openshift/baremetal-provisioning-config.yaml.template index e9a79cac81e..cebcc9d3208 100644 --- a/data/data/manifests/openshift/baremetal-provisioning-config.yaml.template +++ b/data/data/manifests/openshift/baremetal-provisioning-config.yaml.template @@ -6,6 +6,6 @@ spec: provisioningInterface: "{{.Baremetal.ProvisioningNetworkInterface}}" provisioningIP: "{{.Baremetal.ClusterProvisioningIP}}" provisioningNetworkCIDR: "{{.Baremetal.ProvisioningNetworkCIDR}}" - provisioningDHCPExternal: {{.Baremetal.ProvisioningDHCPExternal}} + provisioningNetwork: "{{.Baremetal.ProvisioningNetwork}}" provisioningDHCPRange: "{{.Baremetal.ProvisioningDHCPRange}}" provisioningOSDownloadURL: "{{.ProvisioningOSDownloadURL}}" diff --git a/docs/user/metal/install_ipi.md b/docs/user/metal/install_ipi.md index 3e16570a315..3bdee51dce6 100644 --- a/docs/user/metal/install_ipi.md +++ b/docs/user/metal/install_ipi.md @@ -17,8 +17,10 @@ deployments, see [install_upi.md](install_upi.md). ### Network Requirements -It is assumed that all hosts have at least 2 NICs, used for the following -purposes: +You have the choice of a single or dual NIC setup, depending on whether +you would like to use PXE/DHCP-based provisioning or not. Please note +that disabling the provisioning network means that host BMC's must be +accessible over the external network which may not be desirable. * **NIC #1 - External Network** * This network is the main network used by the cluster, including API traffic @@ -46,12 +48,15 @@ purposes: * `api..` - pointing to the API VIP * `*.apps..` - pointing to the Ingress VIP -* **NIC #2 - Provisioning Network** +* **NIC #2 - Provisioning Network (optional) ** * A private network used for PXE based provisioning. * You must specify `provisioningNetworkInterface` to indicate which - interface is connected to this network. - * DHCP is automated for this network by default, to rely on external - DHCP, set the platform's `provisioningDHCPExternal` option to `true` + interface is connected to this network on the control plane nodes. + * The provisioning network may be "Managed" (default), "Unmanaged," or + "Disabled." + * In managed mode, DHCP and TFTP are configured to run in the cluster. In + unmanaged mode, TFTP is still available but you must configure DHCP + externally. * Addressing for this network defaults to `172.22.0.0/24`, but is configurable by setting the `provisioningNetworkCIDR` option. * Two IP's are required to be available for use, one for the bootstrap @@ -278,6 +283,9 @@ both required. For example To use virtual media instead of PXE for attaching the provisioning image to the host, use `redfish-virtualmedia://` or `idrac-virtualmedia://` +Please note that when the provisioning network is disabled, the only +supported BMC's are virtual media. + ## Work in Progress Integration of the `baremetal` platform is still a work-in-progress across diff --git a/pkg/asset/ignition/bootstrap/baremetal/template.go b/pkg/asset/ignition/bootstrap/baremetal/template.go index 7a92de1f46f..a50c2f0f535 100644 --- a/pkg/asset/ignition/bootstrap/baremetal/template.go +++ b/pkg/asset/ignition/bootstrap/baremetal/template.go @@ -1,12 +1,19 @@ package baremetal import ( - "github.com/openshift/installer/pkg/types/baremetal" + "net" "strings" + + "github.com/openshift/installer/pkg/types" + "github.com/openshift/installer/pkg/types/baremetal" ) // TemplateData holds data specific to templates used for the baremetal platform. type TemplateData struct { + // ProvisioningInterface holds the interface the bootstrap node will use to host the ProvisioningIP below. + // When the provisioning network is disabled, this is the external baremetal network interface. + ProvisioningInterface string + // ProvisioningIP holds the IP the bootstrap node will use to service Ironic, TFTP, etc. ProvisioningIP string @@ -16,6 +23,9 @@ type TemplateData struct { // ProvisioningCIDR has the integer CIDR notation, e.g. 255.255.255.0 should be "24" ProvisioningCIDR int + // ProvisioningDNSMasq determines if we start the dnsmasq service on the bootstrap node. + ProvisioningDNSMasq bool + // ProvisioningDHCPRange has the DHCP range, if DHCP is not external. Otherwise it // should be blank. ProvisioningDHCPRange string @@ -26,17 +36,22 @@ type TemplateData struct { } // GetTemplateData returns platform-specific data for bootstrap templates. -func GetTemplateData(config *baremetal.Platform) *TemplateData { +func GetTemplateData(config *baremetal.Platform, networks []types.MachineNetworkEntry) *TemplateData { var templateData TemplateData templateData.ProvisioningIP = config.BootstrapProvisioningIP - cidr, _ := config.ProvisioningNetworkCIDR.Mask.Size() - templateData.ProvisioningCIDR = cidr - - templateData.ProvisioningIPv6 = config.ProvisioningNetworkCIDR.IP.To4() == nil + if config.ProvisioningNetwork != baremetal.DisabledProvisioningNetwork { + cidr, _ := config.ProvisioningNetworkCIDR.Mask.Size() + templateData.ProvisioningCIDR = cidr + templateData.ProvisioningIPv6 = config.ProvisioningNetworkCIDR.IP.To4() == nil + templateData.ProvisioningInterface = "ens4" + templateData.ProvisioningDNSMasq = true + } - if !config.ProvisioningDHCPExternal { + switch config.ProvisioningNetwork { + case baremetal.ManagedProvisioningNetwork: + // When provisioning network is managed, we set a DHCP range: templateData.ProvisioningDHCPRange = config.ProvisioningDHCPRange var dhcpAllowList []string @@ -46,6 +61,19 @@ func GetTemplateData(config *baremetal.Platform) *TemplateData { } } templateData.ProvisioningDHCPAllowList = strings.Join(dhcpAllowList, " ") + case baremetal.DisabledProvisioningNetwork: + templateData.ProvisioningInterface = "ens3" + templateData.ProvisioningDNSMasq = false + + for _, network := range networks { + if network.CIDR.Contains(net.ParseIP(templateData.ProvisioningIP)) { + templateData.ProvisioningIPv6 = network.CIDR.IP.To4() == nil + + cidr, _ := network.CIDR.Mask.Size() + templateData.ProvisioningCIDR = cidr + } + + } } return &templateData diff --git a/pkg/asset/ignition/bootstrap/baremetal/template_test.go b/pkg/asset/ignition/bootstrap/baremetal/template_test.go index 016dbb72ce7..7be737b8db2 100644 --- a/pkg/asset/ignition/bootstrap/baremetal/template_test.go +++ b/pkg/asset/ignition/bootstrap/baremetal/template_test.go @@ -9,10 +9,10 @@ import ( func TestTemplatingIPv4(t *testing.T) { bareMetalConfig := baremetal.Platform{ - ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.22.0.0/24"), - BootstrapProvisioningIP: "172.22.0.2", - ProvisioningDHCPExternal: false, - ProvisioningDHCPRange: "172.22.0.10,172.22.0.100", + ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.22.0.0/24"), + BootstrapProvisioningIP: "172.22.0.2", + ProvisioningNetwork: baremetal.ManagedProvisioningNetwork, + ProvisioningDHCPRange: "172.22.0.10,172.22.0.100", Hosts: []*baremetal.Host{ { Role: "master", @@ -33,7 +33,7 @@ func TestTemplatingIPv4(t *testing.T) { }, } - result := GetTemplateData(&bareMetalConfig) + result := GetTemplateData(&bareMetalConfig, nil) assert.Equal(t, result.ProvisioningDHCPRange, "172.22.0.10,172.22.0.100") assert.Equal(t, result.ProvisioningCIDR, 24) @@ -44,12 +44,12 @@ func TestTemplatingIPv4(t *testing.T) { func TestTemplatingIPv6(t *testing.T) { bareMetalConfig := baremetal.Platform{ - ProvisioningNetworkCIDR: ipnet.MustParseCIDR("fd2e:6f44:5dd8:b856::0/64"), - BootstrapProvisioningIP: "fd2e:6f44:5dd8:b856::2", - ProvisioningDHCPExternal: true, + ProvisioningNetworkCIDR: ipnet.MustParseCIDR("fd2e:6f44:5dd8:b856::0/64"), + BootstrapProvisioningIP: "fd2e:6f44:5dd8:b856::2", + ProvisioningNetwork: baremetal.UnmanagedProvisioningNetwork, } - result := GetTemplateData(&bareMetalConfig) + result := GetTemplateData(&bareMetalConfig, nil) assert.Equal(t, result.ProvisioningDHCPRange, "") assert.Equal(t, result.ProvisioningCIDR, 64) diff --git a/pkg/asset/ignition/bootstrap/bootstrap.go b/pkg/asset/ignition/bootstrap/bootstrap.go index 9ca1856f03f..84d2c81824b 100644 --- a/pkg/asset/ignition/bootstrap/bootstrap.go +++ b/pkg/asset/ignition/bootstrap/bootstrap.go @@ -245,7 +245,7 @@ func (a *Bootstrap) getTemplateData(installConfig *types.InstallConfig, releaseI switch installConfig.Platform.Name() { case baremetaltypes.Name: - platformData.BareMetal = baremetal.GetTemplateData(installConfig.Platform.BareMetal) + platformData.BareMetal = baremetal.GetTemplateData(installConfig.Platform.BareMetal, installConfig.MachineNetwork) case vspheretypes.Name: platformData.VSphere = vsphere.GetTemplateData(installConfig.Platform.VSphere) } diff --git a/pkg/asset/installconfig/baremetal/baremetal.go b/pkg/asset/installconfig/baremetal/baremetal.go index bb44cd613c5..6728e585a3c 100644 --- a/pkg/asset/installconfig/baremetal/baremetal.go +++ b/pkg/asset/installconfig/baremetal/baremetal.go @@ -3,7 +3,6 @@ package baremetal import ( "fmt" - "github.com/pkg/errors" "gopkg.in/AlecAivazis/survey.v1" "gopkg.in/AlecAivazis/survey.v1/terminal" @@ -15,59 +14,70 @@ import ( // Platform collects bare metal specific configuration. func Platform() (*baremetal.Platform, error) { - var provisioningNetworkCIDR, externalBridge, provisioningBridge, provisioningNetworkInterface string + var provisioningNetworkCIDR, externalBridge, provisioningBridge, provisioningNetwork, provisioningNetworkInterface string + var parsedCIDR *ipnet.IPNet var hosts []*baremetal.Host - if err := survey.Ask([]*survey.Question{ - { - Prompt: &survey.Input{ - Message: "Provisioning Network CIDR", - Help: "The network used for provisioning.", - Default: "172.22.0.0/24", + survey.AskOne(&survey.Select{ + Message: "Provisioning Network", + Help: "Select whether the provisioning network will be managed, unmanaged, or disabled. In managed mode, the cluster deploys DHCP and TFTP services for PXE provisioning.", + Options: []string{"Managed", "Unmanaged", "Disabled"}, + Default: "Managed", + }, &provisioningNetwork, nil) + + if provisioningNetwork != string(baremetal.DisabledProvisioningNetwork) { + if err := survey.Ask([]*survey.Question{ + { + Prompt: &survey.Input{ + Message: "Provisioning Network CIDR", + Help: "The network used for provisioning.", + Default: "172.22.0.0/24", + }, + Validate: survey.ComposeValidators(survey.Required, ipNetValidator), }, - Validate: survey.ComposeValidators(survey.Required, ipNetValidator), - }, - }, &provisioningNetworkCIDR); err != nil { - return nil, err - } - provNetCIDR, err := ipnet.ParseCIDR(provisioningNetworkCIDR) - if err != nil { - return nil, err - } + }, &provisioningNetworkCIDR); err != nil { + return nil, err + } + provNetCIDR, err := ipnet.ParseCIDR(provisioningNetworkCIDR) + if err != nil { + return nil, err + } + parsedCIDR = provNetCIDR - if err := survey.Ask([]*survey.Question{ - { - Prompt: &survey.Input{ - Message: "External bridge", - Help: "External bridge is used for external communication by the bootstrap virtual machine.", - Default: baremetaldefaults.ExternalBridge, + if err := survey.Ask([]*survey.Question{ + { + Prompt: &survey.Input{ + Message: "Provisioning bridge", + Help: "Provisioning bridge is used to provision machines by the bootstrap virtual machine.", + Default: baremetaldefaults.ProvisioningBridge, + }, }, - }, - }, &externalBridge); err != nil { - return nil, err - } + }, &provisioningBridge); err != nil { + return nil, err + } - if err := survey.Ask([]*survey.Question{ - { - Prompt: &survey.Input{ - Message: "Provisioning bridge", - Help: "Provisioning bridge is used to provision machines by the bootstrap virtual machine.", - Default: baremetaldefaults.ProvisioningBridge, + if err := survey.Ask([]*survey.Question{ + { + Prompt: &survey.Input{ + Message: "Provisioning Network Interface", + Help: "The name of the network interface on a control plane host connected to the provisioning network.", + }, + Validate: survey.Required, }, - }, - }, &provisioningBridge); err != nil { - return nil, err + }, &provisioningNetworkInterface); err != nil { + return nil, err + } } if err := survey.Ask([]*survey.Question{ { Prompt: &survey.Input{ - Message: "Provisioning Network Interface", - Help: "The name of the network interface on a control plane host connected to the provisioning network.", + Message: "External bridge", + Help: "External bridge is used for external communication by the bootstrap virtual machine.", + Default: baremetaldefaults.ExternalBridge, }, - Validate: survey.Required, }, - }, &provisioningNetworkInterface); err != nil { + }, &externalBridge); err != nil { return nil, err } @@ -104,7 +114,7 @@ func Platform() (*baremetal.Platform, error) { return &baremetal.Platform{ ExternalBridge: externalBridge, ProvisioningBridge: provisioningBridge, - ProvisioningNetworkCIDR: provNetCIDR, + ProvisioningNetworkCIDR: parsedCIDR, ProvisioningNetworkInterface: provisioningNetworkInterface, Hosts: hosts, }, nil diff --git a/pkg/tfvars/baremetal/baremetal.go b/pkg/tfvars/baremetal/baremetal.go index c05c31328c8..559b696837f 100644 --- a/pkg/tfvars/baremetal/baremetal.go +++ b/pkg/tfvars/baremetal/baremetal.go @@ -18,11 +18,11 @@ import ( ) type config struct { - LibvirtURI string `json:"libvirt_uri,omitempty"` - BootstrapProvisioningIP string `json:"bootstrap_provisioning_ip,omitempty"` - BootstrapOSImage string `json:"bootstrap_os_image,omitempty"` - ExternalBridge string `json:"external_bridge,omitempty"` - ProvisioningBridge string `json:"provisioning_bridge,omitempty"` + LibvirtURI string `json:"libvirt_uri"` + BootstrapProvisioningIP string `json:"bootstrap_provisioning_ip"` + BootstrapOSImage string `json:"bootstrap_os_image"` + ExternalBridge string `json:"external_bridge"` + ProvisioningBridge string `json:"provisioning_bridge"` // Data required for control plane deployment - several maps per host, because of terraform's limitations Hosts []map[string]interface{} `json:"hosts"` diff --git a/pkg/types/baremetal/defaults/platform.go b/pkg/types/baremetal/defaults/platform.go index d78816c6762..d6f7e0cb00a 100644 --- a/pkg/types/baremetal/defaults/platform.go +++ b/pkg/types/baremetal/defaults/platform.go @@ -32,42 +32,56 @@ func SetPlatformDefaults(p *baremetal.Platform, c *types.InstallConfig) { p.LibvirtURI = LibvirtURI } - if p.ProvisioningNetworkCIDR == nil { - p.ProvisioningNetworkCIDR = ipnet.MustParseCIDR(ProvisioningNetworkCIDR) + if p.ProvisioningNetwork == "" { + p.ProvisioningNetwork = baremetal.ManagedProvisioningNetwork } - // If the user doesn't provide an explicit DHCP range, and DHCP is not - // disabled, then we set a default value from the 10th to 100th - // address in the network. - if !p.ProvisioningDHCPExternal && p.ProvisioningDHCPRange == "" { - startIP, _ := cidr.Host(&p.ProvisioningNetworkCIDR.IPNet, 10) - _, broadcastIP := cidr.AddressRange(&p.ProvisioningNetworkCIDR.IPNet) - endIP := cidr.Dec(broadcastIP) - p.ProvisioningDHCPRange = fmt.Sprintf("%s,%s", startIP, endIP) - } + switch p.ProvisioningNetwork { + case baremetal.DisabledProvisioningNetwork: + if p.ClusterProvisioningIP != "" { + for _, network := range c.MachineNetwork { + if network.CIDR.Contains(net.ParseIP(p.ClusterProvisioningIP)) { + p.ProvisioningNetworkCIDR = &network.CIDR + } + } + } + default: + if p.ProvisioningNetworkCIDR == nil { + p.ProvisioningNetworkCIDR = ipnet.MustParseCIDR(ProvisioningNetworkCIDR) + } - if p.BootstrapProvisioningIP == "" { - // Default to the second address in provisioning network, e.g 172.22.0.2 - ip, err := cidr.Host(&p.ProvisioningNetworkCIDR.IPNet, 2) - if err == nil { - p.BootstrapProvisioningIP = ip.String() + if p.BootstrapProvisioningIP == "" { + // Default to the second address in provisioning network, e.g 172.22.0.2 + ip, err := cidr.Host(&p.ProvisioningNetworkCIDR.IPNet, 2) + if err == nil { + p.BootstrapProvisioningIP = ip.String() + } + } + + if p.ClusterProvisioningIP == "" { + // Default to the third address in provisioning network, e.g 172.22.0.3 + ip, err := cidr.Host(&p.ProvisioningNetworkCIDR.IPNet, 3) + if err == nil { + p.ClusterProvisioningIP = ip.String() + } } - } - if p.ClusterProvisioningIP == "" { - // Default to the third address in provisioning network, e.g 172.22.0.3 - ip, err := cidr.Host(&p.ProvisioningNetworkCIDR.IPNet, 3) - if err == nil { - p.ClusterProvisioningIP = ip.String() + if p.ProvisioningBridge == "" { + p.ProvisioningBridge = ProvisioningBridge } } - if p.ExternalBridge == "" { - p.ExternalBridge = ExternalBridge + // If network is managed, and user didn't specify a range, let's use the rest of the subnet range from + // the 10th address until the end. + if p.ProvisioningNetwork == baremetal.ManagedProvisioningNetwork && p.ProvisioningDHCPRange == "" { + startIP, _ := cidr.Host(&p.ProvisioningNetworkCIDR.IPNet, 10) + _, broadcastIP := cidr.AddressRange(&p.ProvisioningNetworkCIDR.IPNet) + endIP := cidr.Dec(broadcastIP) + p.ProvisioningDHCPRange = fmt.Sprintf("%s,%s", startIP, endIP) } - if p.ProvisioningBridge == "" { - p.ProvisioningBridge = ProvisioningBridge + if p.ExternalBridge == "" { + p.ExternalBridge = ExternalBridge } for _, host := range p.Hosts { diff --git a/pkg/types/baremetal/defaults/platform_test.go b/pkg/types/baremetal/defaults/platform_test.go index ba5ccbc4f57..0ebe4ead970 100644 --- a/pkg/types/baremetal/defaults/platform_test.go +++ b/pkg/types/baremetal/defaults/platform_test.go @@ -28,6 +28,8 @@ func TestSetPlatformDefaults(t *testing.T) { } } + machineNetwork := ipnet.MustParseCIDR("192.168.111.0/24") + cases := []struct { name string platform *baremetal.Platform @@ -42,6 +44,7 @@ func TestSetPlatformDefaults(t *testing.T) { BootstrapProvisioningIP: "172.22.0.2", ExternalBridge: "baremetal", ProvisioningBridge: "provisioning", + ProvisioningNetwork: baremetal.ManagedProvisioningNetwork, APIVIP: "192.168.111.2", IngressVIP: "192.168.111.3", ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.22.0.0/24"), @@ -59,6 +62,7 @@ func TestSetPlatformDefaults(t *testing.T) { BootstrapProvisioningIP: "172.23.0.2", ExternalBridge: "baremetal", ProvisioningBridge: "provisioning", + ProvisioningNetwork: baremetal.ManagedProvisioningNetwork, APIVIP: "192.168.111.2", IngressVIP: "192.168.111.3", ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.23.0.0/24"), @@ -76,6 +80,7 @@ func TestSetPlatformDefaults(t *testing.T) { BootstrapProvisioningIP: "fd2e:6f44:5dd8:b856::2", ExternalBridge: "baremetal", ProvisioningBridge: "provisioning", + ProvisioningNetwork: baremetal.ManagedProvisioningNetwork, APIVIP: "192.168.111.2", IngressVIP: "192.168.111.3", ProvisioningNetworkCIDR: ipnet.MustParseCIDR("fd2e:6f44:5dd8:b856::/64"), @@ -85,28 +90,55 @@ func TestSetPlatformDefaults(t *testing.T) { { name: "alternate_cidr_dhcp_disabled", platform: &baremetal.Platform{ - ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.23.0.0/24"), - ProvisioningDHCPExternal: true, + ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.23.0.0/24"), + ProvisioningNetwork: baremetal.UnmanagedProvisioningNetwork, + }, + expected: &baremetal.Platform{ + LibvirtURI: "qemu:///system", + ClusterProvisioningIP: "172.23.0.3", + BootstrapProvisioningIP: "172.23.0.2", + ExternalBridge: "baremetal", + ProvisioningBridge: "provisioning", + APIVIP: "192.168.111.2", + IngressVIP: "192.168.111.3", + ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.23.0.0/24"), + ProvisioningNetwork: baremetal.UnmanagedProvisioningNetwork, + }, + }, + { + name: "disabled_provisioning_network", + platform: &baremetal.Platform{ + ProvisioningNetwork: baremetal.DisabledProvisioningNetwork, + BootstrapProvisioningIP: "192.168.111.7", + ClusterProvisioningIP: "192.168.111.8", }, expected: &baremetal.Platform{ - LibvirtURI: "qemu:///system", - ClusterProvisioningIP: "172.23.0.3", - BootstrapProvisioningIP: "172.23.0.2", - ExternalBridge: "baremetal", - ProvisioningBridge: "provisioning", - APIVIP: "192.168.111.2", - IngressVIP: "192.168.111.3", - ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.23.0.0/24"), - ProvisioningDHCPExternal: true, + BootstrapProvisioningIP: "192.168.111.7", + ClusterProvisioningIP: "192.168.111.8", + LibvirtURI: "qemu:///system", + ExternalBridge: "baremetal", + ProvisioningBridge: "", + ProvisioningNetwork: baremetal.DisabledProvisioningNetwork, + ProvisioningNetworkCIDR: machineNetwork, + APIVIP: "192.168.111.2", + IngressVIP: "192.168.111.3", }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + ic := &types.InstallConfig{ ObjectMeta: metav1.ObjectMeta{ Name: testClusterName, }, + Networking: &types.Networking{ + MachineNetwork: []types.MachineNetworkEntry{ + { + CIDR: ipnet.IPNet{IPNet: machineNetwork.IPNet}, + }, + }, + }, BaseDomain: "test", } SetPlatformDefaults(tc.platform, ic) diff --git a/pkg/types/baremetal/platform.go b/pkg/types/baremetal/platform.go index bde97a6d5fb..092909be895 100644 --- a/pkg/types/baremetal/platform.go +++ b/pkg/types/baremetal/platform.go @@ -35,6 +35,27 @@ type Host struct { BootMode BootMode `json:"bootMode,omitempty"` } +// ProvisioningNetwork determines how we will use the provisioning network. +// +kubebuilder:validation:Enum="";Managed;Unmanaged;Disabled +type ProvisioningNetwork string + +const ( + // ManagedProvisioningNetwork indicates we should fully manage the provisioning network, including DHCP + // services required for PXE-based provisioning. + ManagedProvisioningNetwork ProvisioningNetwork = "Managed" + + // UnmanagedProvisioningNetwork indicates responsibility for managing the provisioning network is left to the + // user. No DHCP server will be configured, however TFTP remains enabled if a user wants to use PXE-based provisioning. + // However, they will need to configure external DHCP correctly with next-server definitions set to the relevant + // provisioning IP's. + UnmanagedProvisioningNetwork ProvisioningNetwork = "Unmanaged" + + // DisabledProvisioningNetwork indicates that no provisioning network will be used. Provisioning capabilities + // will be limited to virtual media-based deployments only, and neither DHCP nor TFTP will be operated by the + // cluster. + DisabledProvisioningNetwork ProvisioningNetwork = "Disabled" +) + // Platform stores all the global configuration that all machinesets use. type Platform struct { // LibvirtURI is the identifier for the libvirtd connection. It must be @@ -63,6 +84,11 @@ type Platform struct { // +optional ExternalBridge string `json:"externalBridge,omitempty"` + // ProvisioningNetwork is used to indicate if we will have a provisioning network, and how it will be managed. + // +kubebuilder:default=Managed + // +optional + ProvisioningNetwork ProvisioningNetwork `json:"provisioningNetwork,omitempty"` + // Provisioning bridge is used for provisioning nodes, on the host that // will run the bootstrap VM. // +optional @@ -76,12 +102,10 @@ type Platform struct { // +optional ProvisioningNetworkCIDR *ipnet.IPNet `json:"provisioningNetworkCIDR,omitempty"` - // ProvisioningDHCPExternal indicates that DHCP is provided by an external service, appropriately - // configured with next-server set to BootstrapProvisioningIP for the control plane, and - // ClusterProvisioningIP for workers. The default for this field is false, which means we will - // start and manage a DHCP server on the provisioning network. + // DeprecatedProvisioningDHCPExternal indicates that DHCP is provided by an external service. This parameter is + // replaced by ProvisioningNetwork being set to "Unmanaged". // +optional - ProvisioningDHCPExternal bool `json:"provisioningDHCPExternal,omitempty"` + DeprecatedProvisioningDHCPExternal bool `json:"provisioningDHCPExternal,omitempty"` // ProvisioningDHCPRange is used to provide DHCP services to hosts // for provisioning. diff --git a/pkg/types/baremetal/validation/libvirt.go b/pkg/types/baremetal/validation/libvirt.go index 4e0824951a9..ac7650ab3b7 100644 --- a/pkg/types/baremetal/validation/libvirt.go +++ b/pkg/types/baremetal/validation/libvirt.go @@ -29,7 +29,7 @@ func validateInterfaces(p *baremetal.Platform, fldPath *field.Path) field.ErrorL errorList = append(errorList, field.Invalid(fldPath.Child("externalBridge"), p.ExternalBridge, err.Error())) } - if err := findInterface(p.ProvisioningBridge); err != nil { + if err := findInterface(p.ProvisioningBridge); p.ProvisioningNetwork != baremetal.DisabledProvisioningNetwork && err != nil { errorList = append(errorList, field.Invalid(fldPath.Child("provisioningBridge"), p.ProvisioningBridge, err.Error())) } diff --git a/pkg/types/baremetal/validation/platform.go b/pkg/types/baremetal/validation/platform.go index 471d25195d2..dd07ab48ebe 100644 --- a/pkg/types/baremetal/validation/platform.go +++ b/pkg/types/baremetal/validation/platform.go @@ -12,12 +12,13 @@ import ( "github.com/apparentlymart/go-cidr/cidr" "github.com/go-playground/validator/v10" "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation/field" "github.com/openshift/installer/pkg/ipnet" "github.com/openshift/installer/pkg/types" "github.com/openshift/installer/pkg/types/baremetal" "github.com/openshift/installer/pkg/validate" - "k8s.io/apimachinery/pkg/util/validation/field" ) // dynamicProvisioningValidator is a function that validates certain fields in the platform. @@ -29,12 +30,16 @@ type dynamicProvisioningValidator func(*baremetal.Platform, *field.Path) field.E var dynamicProvisioningValidators []dynamicProvisioningValidator func validateIPinMachineCIDR(vip string, n *types.Networking) error { + var networks []string + for _, network := range n.MachineNetwork { if network.CIDR.Contains(net.ParseIP(vip)) { return nil } + networks = append(networks, network.CIDR.String()) } - return fmt.Errorf("the virtual IP is expected to be in one of the machine networks") + + return fmt.Errorf("IP expected to be in one of the machine networks: %s", strings.Join(networks, ",")) } func validateIPNotinMachineCIDR(ip string, n *types.Networking) error { @@ -289,6 +294,15 @@ func validateBootMode(hosts []*baremetal.Host, fldPath *field.Path) (errors fiel // ValidatePlatform checks that the specified platform is valid. func ValidatePlatform(p *baremetal.Platform, n *types.Networking, fldPath *field.Path, c *types.InstallConfig) field.ErrorList { allErrs := field.ErrorList{} + + provisioningNetwork := sets.NewString(string(baremetal.ManagedProvisioningNetwork), + string(baremetal.UnmanagedProvisioningNetwork), + string(baremetal.DisabledProvisioningNetwork)) + + if !provisioningNetwork.Has(string(p.ProvisioningNetwork)) { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("provisioningNetwork"), p.ProvisioningNetwork, provisioningNetwork.List())) + } + if err := validate.IP(p.ClusterProvisioningIP); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningHostIP"), p.ClusterProvisioningIP, err.Error())) } @@ -332,14 +346,21 @@ func ValidatePlatform(p *baremetal.Platform, n *types.Networking, fldPath *field func ValidateProvisioning(p *baremetal.Platform, n *types.Networking, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - if err := validate.IP(p.BootstrapProvisioningIP); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapProvisioningIP"), p.BootstrapProvisioningIP, err.Error())) - } - if err := validateIPNotinMachineCIDR(p.ClusterProvisioningIP, n); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningHostIP"), p.ClusterProvisioningIP, err.Error())) - } + switch p.ProvisioningNetwork { + // If we do not have a provisioning network, provisioning services + // will be run on the external network. Users must provide IP's on the + // machine networks to host those services. + case baremetal.DisabledProvisioningNetwork: + // Ensure bootstrapProvisioningIP is in one of the machine networks + if err := validateIPinMachineCIDR(p.BootstrapProvisioningIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapProvisioningIP"), p.BootstrapProvisioningIP, fmt.Sprintf("provisioning network is disabled, %s", err.Error()))) + } - if p.ProvisioningNetworkCIDR != nil { + // Ensure clusterProvisioningIP is in one of the machine networks + if err := validateIPinMachineCIDR(p.ClusterProvisioningIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterProvisioningIP"), p.ClusterProvisioningIP, fmt.Sprintf("provisioning network is disabled, %s", err.Error()))) + } + default: // Ensure provisioningNetworkCIDR doesn't overlap with any machine network if err := validateNoOverlapMachineCIDR(&p.ProvisioningNetworkCIDR.IPNet, n); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningNetworkCIDR"), p.ProvisioningNetworkCIDR.String(), err.Error())) @@ -354,19 +375,18 @@ func ValidateProvisioning(p *baremetal.Platform, n *types.Networking, fldPath *f if !p.ProvisioningNetworkCIDR.Contains(net.ParseIP(p.ClusterProvisioningIP)) { allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterProvisioningIP"), p.ClusterProvisioningIP, fmt.Sprintf("%q is not in the provisioning network", p.ClusterProvisioningIP))) } - } - - if p.ProvisioningDHCPRange != "" { - allErrs = append(allErrs, validateDHCPRange(p, fldPath)...) - } - if err := validateIPNotinMachineCIDR(p.BootstrapProvisioningIP, n); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapHostIP"), p.BootstrapProvisioningIP, err.Error())) - } + if err := validateIPNotinMachineCIDR(p.BootstrapProvisioningIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapProvisioningIP"), p.BootstrapProvisioningIP, err.Error())) + } - // Make sure the provisioning interface is set. Very little we can do to validate this as it's not on this machine. - if p.ProvisioningNetworkInterface == "" { - allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningNetworkInterface"), p.ProvisioningNetworkInterface, "no provisioning network interface is configured, please set this value to be the interface on the provisioning network on your cluster's baremetal hosts")) + if p.ProvisioningDHCPRange != "" { + allErrs = append(allErrs, validateDHCPRange(p, fldPath)...) + } + // Make sure the provisioning interface is set. Very little we can do to validate this as it's not on this machine. + if p.ProvisioningNetworkInterface == "" { + allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningNetworkInterface"), p.ProvisioningNetworkInterface, "no provisioning network interface is configured, please set this value to be the interface on the provisioning network on your cluster's baremetal hosts")) + } } if err := validate.URI(p.LibvirtURI); err != nil { diff --git a/pkg/types/baremetal/validation/platform_test.go b/pkg/types/baremetal/validation/platform_test.go index 116f94b32b8..6c8dad59e99 100644 --- a/pkg/types/baremetal/validation/platform_test.go +++ b/pkg/types/baremetal/validation/platform_test.go @@ -45,13 +45,13 @@ func TestValidatePlatform(t *testing.T) { name: "invalid_apivip", platform: platform(). APIVIP("192.168.222.2").build(), - expected: "Invalid value: \"192.168.222.2\": the virtual IP is expected to be in one of the machine networks", + expected: "Invalid value: \"192.168.222.2\": IP expected to be in one of the machine networks: 192.168.111.0/24", }, { name: "invalid_ingressvip", platform: platform(). IngressVIP("192.168.222.4").build(), - expected: "Invalid value: \"192.168.222.4\": the virtual IP is expected to be in one of the machine networks", + expected: "Invalid value: \"192.168.222.4\": IP expected to be in one of the machine networks: 192.168.111.0/24", }, { name: "invalid_hosts", @@ -128,6 +128,19 @@ func TestValidatePlatform(t *testing.T) { Hosts(host1().BootMode("legacy")).build(), expected: "", }, + { + name: "provisioningNetwork_disabled_valid", + platform: platform().ProvisioningNetwork(baremetal.DisabledProvisioningNetwork).build(), + }, + { + name: "provisioningNetwork_unmanaged_valid", + platform: platform().ProvisioningNetwork(baremetal.UnmanagedProvisioningNetwork).build(), + }, + { + name: "provisioningNetwork_invalid", + platform: platform().ProvisioningNetwork("Invalid").build(), + expected: `Unsupported value: "Invalid": supported values: "Disabled", "Managed", "Unmanaged"`, + }, } for _, tc := range cases { @@ -283,13 +296,14 @@ func TestValidateProvisioning(t *testing.T) { name: "invalid_bootstrapprovip_machineCIDR", platform: platform(). BootstrapProvisioningIP("192.168.111.5").build(), - expected: "Invalid value: \"192.168.111.5\": the IP must not be in one of the machine networks", + expected: "Invalid value: \"192.168.111.5\": \"192.168.111.5\" is not in the provisioning network", }, + { name: "invalid_clusterprovip_machineCIDR", platform: platform(). ClusterProvisioningIP("192.168.111.5").build(), - expected: "Invalid value: \"192.168.111.5\": the IP must not be in one of the machine networks", + expected: "Invalid value: \"192.168.111.5\": \"192.168.111.5\" is not in the provisioning network", }, { name: "invalid_clusterprovip_wrongCIDR", @@ -349,6 +363,34 @@ func TestValidateProvisioning(t *testing.T) { LibvirtURI("bad").build(), expected: "invalid URI \"bad\"", }, + + // Disabled provisioning network + { + name: "valid_provisioningDisabled_noProvisioningInterface", + config: installConfig().Network(networking().Network("192.168.111.0/24")).build(), + platform: platform(). + ProvisioningNetwork(baremetal.DisabledProvisioningNetwork). + ClusterProvisioningIP("192.168.111.2"). + BootstrapProvisioningIP("192.168.111.3"). + ProvisioningNetworkInterface("").build(), + }, + { + name: "valid_provisioningDisabled_IPs_in_machineCIDR", + config: installConfig().Network(networking().Network("192.168.111.0/24")).build(), + platform: platform(). + ProvisioningNetwork(baremetal.DisabledProvisioningNetwork). + ClusterProvisioningIP("192.168.111.2"). + BootstrapProvisioningIP("192.168.111.3").build(), + }, + { + name: "invalid_provisioningDisabled_IPs_not_in_machineCIDR", + config: installConfig().Network(networking().Network("192.168.111.0/24")).build(), + platform: platform(). + ProvisioningNetwork(baremetal.DisabledProvisioningNetwork). + BootstrapProvisioningIP("192.168.111.3"). + ClusterProvisioningIP("192.168.0.2").build(), + expected: "Invalid value: \"192.168.0.2\": provisioning network is disabled, IP expected to be in one of the machine networks: 192.168.111.0/24", + }, } for _, tc := range cases { @@ -356,8 +398,8 @@ func TestValidateProvisioning(t *testing.T) { //Build default wrapping installConfig if tc.config == nil { tc.config = installConfig().build() - tc.config.BareMetal = tc.platform } + tc.config.BareMetal = tc.platform defaults.SetPlatformDefaults(tc.config.BareMetal, tc.config) @@ -450,6 +492,7 @@ func platform() *platformBuilder { Hosts: []*baremetal.Host{}, LibvirtURI: "qemu://system", ProvisioningNetworkCIDR: ipnet.MustParseCIDR("172.22.0.0/24"), + ProvisioningNetwork: baremetal.ManagedProvisioningNetwork, ClusterProvisioningIP: "172.22.0.3", BootstrapProvisioningIP: "172.22.0.2", ExternalBridge: "br0", @@ -467,6 +510,11 @@ func (pb *platformBuilder) ProvisioningNetworkCIDR(value string) *platformBuilde return pb } +func (pb *platformBuilder) ProvisioningNetwork(value baremetal.ProvisioningNetwork) *platformBuilder { + pb.Platform.ProvisioningNetwork = value + return pb +} + func (pb *platformBuilder) ClusterProvisioningIP(value string) *platformBuilder { pb.Platform.ClusterProvisioningIP = value return pb @@ -561,6 +609,12 @@ func (icb *installConfigBuilder) ControlPlane(builder *machinePoolBuilder) *inst return icb } +func (icb *installConfigBuilder) Network(builder *networkingBuilder) *installConfigBuilder { + icb.InstallConfig.Networking = builder.build() + + return icb +} + func (icb *installConfigBuilder) Compute(builders ...*machinePoolBuilder) *installConfigBuilder { icb.InstallConfig.Compute = nil for _, builder := range builders { @@ -587,3 +641,27 @@ func (mpb *machinePoolBuilder) Replicas(count int64) *machinePoolBuilder { mpb.MachinePool.Replicas = &count return mpb } + +type networkingBuilder struct { + types.Networking +} + +func networking() *networkingBuilder { + return &networkingBuilder{ + Networking: types.Networking{}, + } +} + +func (nb *networkingBuilder) Network(cidr string) *networkingBuilder { + network := ipnet.MustParseCIDR(cidr) + + nb.MachineNetwork = append(nb.MachineNetwork, types.MachineNetworkEntry{ + CIDR: *network, + }) + + return nb +} + +func (nb *networkingBuilder) build() *types.Networking { + return &nb.Networking +} diff --git a/pkg/types/conversion/installconfig.go b/pkg/types/conversion/installconfig.go index 29fb0057328..03d807c01a2 100644 --- a/pkg/types/conversion/installconfig.go +++ b/pkg/types/conversion/installconfig.go @@ -7,6 +7,7 @@ import ( "github.com/openshift/installer/pkg/ipnet" "github.com/openshift/installer/pkg/types" + "github.com/openshift/installer/pkg/types/baremetal" ) // ConvertInstallConfig is modeled after the k8s conversion schemes, which is @@ -25,6 +26,11 @@ func ConvertInstallConfig(config *types.InstallConfig) error { } ConvertNetworking(config) + switch config.Platform.Name() { + case baremetal.Name: + ConvertBaremetal(config) + } + config.APIVersion = types.InstallConfigVersion return nil } @@ -64,3 +70,12 @@ func ConvertNetworking(config *types.InstallConfig) { } } } + +// ConvertBaremetal upconverts deprecated fields in the baremetal +// platform. ProvisioningDHCPExternal has been replaced by setting +// the ProvisioningNetwork field to "Unmanaged" +func ConvertBaremetal(config *types.InstallConfig) { + if config.Platform.BareMetal.DeprecatedProvisioningDHCPExternal == true && config.Platform.BareMetal.ProvisioningNetwork == "" { + config.Platform.BareMetal.ProvisioningNetwork = baremetal.UnmanagedProvisioningNetwork + } +} diff --git a/pkg/types/validation/installconfig_test.go b/pkg/types/validation/installconfig_test.go index fae4ac7674e..20de35c7062 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -95,6 +95,7 @@ func validBareMetalPlatform() *baremetal.Platform { ProvisioningNetworkCIDR: ipnet.MustParseCIDR("192.168.111.0/24"), BootstrapProvisioningIP: "192.168.111.1", ClusterProvisioningIP: "192.168.111.2", + ProvisioningNetwork: baremetal.ManagedProvisioningNetwork, Hosts: []*baremetal.Host{ { Name: "host1", @@ -598,7 +599,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform.BareMetal.APIVIP = "" return c }(), - expectedError: `^\[platform\.baremetal\.apiVIP: Invalid value: "": "" is not a valid IP, platform\.baremetal\.apiVIP: Invalid value: "": the virtual IP is expected to be in one of the machine networks]$`, + expectedError: `^\[platform\.baremetal\.apiVIP: Invalid value: "": "" is not a valid IP, platform\.baremetal\.apiVIP: Invalid value: "": IP expected to be in one of the machine networks: 10.0.0.0/16]$`, }, { name: "baremetal API VIP not an IP", @@ -610,7 +611,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform.BareMetal.APIVIP = "test" return c }(), - expectedError: `^\[platform\.baremetal\.apiVIP: Invalid value: "test": "test" is not a valid IP, platform\.baremetal\.apiVIP: Invalid value: "test": the virtual IP is expected to be in one of the machine networks]$`, + expectedError: `^\[platform\.baremetal\.apiVIP: Invalid value: "test": "test" is not a valid IP, platform\.baremetal\.apiVIP: Invalid value: "test": IP expected to be in one of the machine networks: 10.0.0.0/16]$`, }, { name: "baremetal API VIP set to an incorrect value", @@ -622,7 +623,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform.BareMetal.APIVIP = "10.1.0.5" return c }(), - expectedError: `^platform\.baremetal\.apiVIP: Invalid value: "10\.1\.0\.5": the virtual IP is expected to be in one of the machine networks$`, + expectedError: `^platform\.baremetal\.apiVIP: Invalid value: "10\.1\.0\.5": IP expected to be in one of the machine networks: 10.0.0.0/16$`, }, { name: "baremetal Ingress VIP not an IP", @@ -634,7 +635,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform.BareMetal.IngressVIP = "test" return c }(), - expectedError: `^\[platform\.baremetal\.ingressVIP: Invalid value: "test": "test" is not a valid IP, platform\.baremetal\.ingressVIP: Invalid value: "test": the virtual IP is expected to be in one of the machine networks]$`, + expectedError: `^\[platform\.baremetal\.ingressVIP: Invalid value: "test": "test" is not a valid IP, platform\.baremetal\.ingressVIP: Invalid value: "test": IP expected to be in one of the machine networks: 10.0.0.0/16]$`, }, { name: "baremetal Ingress VIP set to an incorrect value", @@ -646,7 +647,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform.BareMetal.IngressVIP = "10.1.0.7" return c }(), - expectedError: `^platform\.baremetal\.ingressVIP: Invalid value: "10\.1\.0\.7": the virtual IP is expected to be in one of the machine networks$`, + expectedError: `^platform\.baremetal\.ingressVIP: Invalid value: "10\.1\.0\.7": IP expected to be in one of the machine networks: 10.0.0.0/16$`, }, { name: "valid vsphere platform", installConfig: func() *types.InstallConfig {