Skip to content

Commit c7e06e6

Browse files
Merge pull request #1162 from honza/external-url-ipv6
Set external_http_url on the node when BMC is IPv6
2 parents 853f26c + a737b7a commit c7e06e6

4 files changed

Lines changed: 156 additions & 0 deletions

File tree

docs/configuration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ but overflows could happen in case of slow provisioners and / or higher number o
4444
concurrent reconciles. For such reasons, it is highly recommended to keep
4545
BMO_CONCURRENCY value lower than the requested PROVISIONING_LIMIT. Default is 20.
4646

47+
`IRONIC_EXTERNAL_URL_V6` -- This is the URL where Ironic will find the image for
48+
nodes that use IPv6. In dual stack environments, this can be used to tell Ironic which IP
49+
version it should set on the BMC.
50+
4751
Kustomization Configuration
4852
---------------------------
4953

pkg/provisioner/ironic/factory.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ironic
33
import (
44
"errors"
55
"fmt"
6+
"net/url"
67
"os"
78
"strconv"
89
"strings"
@@ -153,6 +154,17 @@ func loadConfigFromEnv(havePreprovImgBuilder bool) (ironicConfig, error) {
153154
c.liveISOForcePersistentBootDevice = forcePersistentBootDevice
154155
}
155156

157+
c.externalURL = os.Getenv("IRONIC_EXTERNAL_URL_V6")
158+
159+
// Let's see if externalURL looks like a URL
160+
if c.externalURL != "" {
161+
_, externalURLParseErr := url.Parse(c.externalURL)
162+
163+
if externalURLParseErr != nil {
164+
return c, externalURLParseErr
165+
}
166+
}
167+
156168
return c, nil
157169
}
158170

pkg/provisioner/ironic/ironic.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ironic
22

33
import (
44
"fmt"
5+
"net"
56
"strings"
67
"time"
78

@@ -75,6 +76,7 @@ type ironicConfig struct {
7576
deployISOURL string
7677
liveISOForcePersistentBootDevice string
7778
maxBusyHosts int
79+
externalURL string
7880
}
7981

8082
// Provisioner implements the provisioning.Provisioner interface
@@ -352,6 +354,7 @@ func (p *ironicProvisioner) ValidateManagementAccess(data provisioner.Management
352354
}
353355

354356
driverInfo := bmcAccess.DriverInfo(p.bmcCreds)
357+
driverInfo = setExternalURL(p, driverInfo)
355358
deployImageInfo := setDeployImage(driverInfo, p.config, bmcAccess, data.PreprovisioningImage)
356359

357360
// If we have not found a node yet, we need to create one
@@ -578,6 +581,48 @@ func (p *ironicProvisioner) PreprovisioningImageFormats() ([]metal3v1alpha1.Imag
578581
return formats, nil
579582
}
580583

584+
func setExternalURL(p *ironicProvisioner, driverInfo map[string]interface{}) map[string]interface{} {
585+
if _, ok := driverInfo["external_http_url"]; ok {
586+
driverInfo["external_http_url"] = nil
587+
}
588+
589+
if p.config.externalURL == "" {
590+
return driverInfo
591+
}
592+
593+
parsedURL, err := bmc.GetParsedURL(p.bmcAddress)
594+
if err != nil {
595+
p.log.Info("Failed to parse BMC address", "bmcAddress", p.bmcAddress, "err", err)
596+
return driverInfo
597+
}
598+
599+
ip := net.ParseIP(parsedURL.Hostname())
600+
if ip == nil {
601+
// Maybe it's a hostname?
602+
ips, err := net.LookupIP(p.bmcAddress)
603+
if err != nil {
604+
p.log.Info("Failed to look up the IP address for BMC hostname", "hostname", p.bmcAddress)
605+
return driverInfo
606+
}
607+
608+
if len(ips) == 0 {
609+
p.log.Info("Zero IP addresses for BMC hostname", "hostname", p.bmcAddress)
610+
return driverInfo
611+
}
612+
613+
ip = ips[0]
614+
}
615+
616+
// In the case of IPv4, we don't have to do anything.
617+
if ip.To4() != nil {
618+
return driverInfo
619+
}
620+
621+
driverInfo["external_http_url"] = p.config.externalURL
622+
623+
return driverInfo
624+
}
625+
581626
func setDeployImage(driverInfo map[string]interface{}, config ironicConfig, accessDetails bmc.AccessDetails, hostImage *provisioner.PreprovisioningImage) optionsData {
582627
deployImageInfo := optionsData{
583628
deployKernelKey: nil,

pkg/provisioner/ironic/validatemanagementaccess_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,3 +1148,98 @@ func TestSetDeployImage(t *testing.T) {
11481148
})
11491149
}
11501150
}
1151+
1152+
func TestSetExternalURL(t *testing.T) {
1153+
host := makeHost()
1154+
host.Spec.BMC.Address = "redfish-virtualmedia://[fe80::fc33:62ff:fe83:8a76]:6233"
1155+
1156+
ironic := testserver.NewIronic(t).Ready().
1157+
Node(nodes.Node{
1158+
Name: host.Namespace + nameSeparator + host.Name,
1159+
UUID: host.Status.Provisioning.ID,
1160+
}).NodeUpdate(nodes.Node{
1161+
UUID: host.Status.Provisioning.ID,
1162+
})
1163+
1164+
ironic.Start()
1165+
defer ironic.Stop()
1166+
1167+
auth := clients.AuthConfig{Type: clients.NoAuth}
1168+
prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nil,
1169+
ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth,
1170+
)
1171+
1172+
if err != nil {
1173+
t.Fatalf("could not create provisioner: %s", err)
1174+
}
1175+
1176+
prov.config.externalURL = "XXX"
1177+
1178+
driverInfo := make(map[string]interface{}, 0)
1179+
updatedDriverInfo := setExternalURL(prov, driverInfo)
1180+
1181+
assert.Equal(t, "XXX", updatedDriverInfo["external_http_url"])
1182+
}
1183+
1184+
func TestSetExternalURLIPv4(t *testing.T) {
1185+
host := makeHost()
1186+
host.Spec.BMC.Address = "redfish-virtualmedia://1.1.1.1:1111"
1187+
1188+
ironic := testserver.NewIronic(t).Ready().
1189+
Node(nodes.Node{
1190+
Name: host.Namespace + nameSeparator + host.Name,
1191+
UUID: host.Status.Provisioning.ID,
1192+
}).NodeUpdate(nodes.Node{
1193+
UUID: host.Status.Provisioning.ID,
1194+
})
1195+
1196+
ironic.Start()
1197+
defer ironic.Stop()
1198+
1199+
auth := clients.AuthConfig{Type: clients.NoAuth}
1200+
prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nil,
1201+
ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth,
1202+
)
1203+
1204+
if err != nil {
1205+
t.Fatalf("could not create provisioner: %s", err)
1206+
}
1207+
1208+
prov.config.externalURL = "XXX"
1209+
1210+
driverInfo := make(map[string]interface{}, 0)
1211+
updatedDriverInfo := setExternalURL(prov, driverInfo)
1212+
1213+
assert.Equal(t, nil, updatedDriverInfo["external_http_url"])
1214+
}
1215+
1216+
func TestSetExternalURLRemoving(t *testing.T) {
1217+
host := makeHost()
1218+
host.Spec.BMC.Address = "redfish-virtualmedia://1.1.1.1:1111"
1219+
1220+
ironic := testserver.NewIronic(t).Ready().
1221+
Node(nodes.Node{
1222+
Name: host.Namespace + nameSeparator + host.Name,
1223+
UUID: host.Status.Provisioning.ID,
1224+
}).NodeUpdate(nodes.Node{
1225+
UUID: host.Status.Provisioning.ID,
1226+
})
1227+
1228+
ironic.Start()
1229+
defer ironic.Stop()
1230+
1231+
auth := clients.AuthConfig{Type: clients.NoAuth}
1232+
prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nil,
1233+
ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth,
1234+
)
1235+
1236+
if err != nil {
1237+
t.Fatalf("could not create provisioner: %s", err)
1238+
}
1239+
1240+
driverInfo := make(map[string]interface{}, 0)
1241+
driverInfo["external_http_url"] = "non-empty"
1242+
updatedDriverInfo := setExternalURL(prov, driverInfo)
1243+
1244+
assert.Equal(t, nil, updatedDriverInfo["external_http_url"])
1245+
}

0 commit comments

Comments
 (0)