Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 79 additions & 7 deletions pkg/platforms/openstack/openstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
network "net"
"os"
"os/exec"
"path/filepath"
Expand All @@ -26,11 +27,10 @@ const (
varConfigPath = "/var/config"
ospMetaDataBaseDir = "/openstack/2018-08-27"
ospMetaDataDir = varConfigPath + ospMetaDataBaseDir
ospMetaDataBaseURL = "http://169.254.169.254" + ospMetaDataBaseDir
ospNetworkDataJSON = "network_data.json"
ospMetaDataJSON = "meta_data.json"
ospNetworkDataURL = ospMetaDataBaseURL + "/" + ospNetworkDataJSON
ospMetaDataURL = ospMetaDataBaseURL + "/" + ospMetaDataJSON
ospHTTP = "http://"
ospHTTPS = "https://"
// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
//https://docs.openstack.org/nova/latest/user/config-drive.html
Expand All @@ -40,6 +40,10 @@ const (
var (
ospNetworkDataFile = ospMetaDataDir + "/" + ospNetworkDataJSON
ospMetaDataFile = ospMetaDataDir + "/" + ospMetaDataJSON
ospBaseURLS = [2]string{"169.254.169.254", "fe80::a9fe:a9fe"}
ospNetworkDataURL string
ospMetaDataURL string
ospMetaDataBaseURL []string
)

//go:generate ../../../bin/mockgen -destination mock/mock_openstack.go -source openstack.go
Expand Down Expand Up @@ -113,17 +117,85 @@ func New(hostManager host.HostManagerInterface) OpenstackInterface {
}
}

func getActiveInterfaceName() (string, error) {
interfaces, err := network.Interfaces()
if err != nil {
return "", err
}

for _, intf := range interfaces {
// Check if the interface is up and not a loopback
if intf.Flags&network.FlagUp != 0 && intf.Flags&network.FlagLoopback == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check if there is at least one IPv6 address defined in the interface to be used

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay i added this check thanks for the feedback

addrs, err := intf.Addrs()
if err != nil {
continue
}

// Check if at least one address is an IPv6 address
for _, addr := range addrs {
if ipNet, ok := addr.(*network.IPNet); ok && ipNet.IP.To4() == nil {
return intf.Name, nil
}
}
}
}

return "", fmt.Errorf("no active non-loopback interface found with an IPv6 address")
}

func constructMetaDataURLs(baseURL, activeInterface string, isIPv6 bool) []string {
var urls []string
encodedSign := "%25"
port := "80"
if isIPv6 {
urls = append(urls, ospHTTPS+"["+baseURL+encodedSign+activeInterface+"]:"+port)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi question can you please explain why we need the interface name here?

base on this we can just send a request to the ipv6 address and it should be good no?

https://specs.openstack.org/openstack/neutron-specs/specs/ussuri/metadata-add-ipv6-support.html

Copy link
Author

@dkokkino dkokkino Mar 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get it thanks!

urls = append(urls, ospHTTP+"["+baseURL+encodedSign+activeInterface+"]:"+port)
} else {
urls = append(urls, ospHTTPS+baseURL)
urls = append(urls, ospHTTP+baseURL)
}
return urls
}

// GetOpenstackData gets the metadata and network_data
func getOpenstackData(mountConfigDrive bool) (metaData *OSPMetaData, networkData *OSPNetworkData, err error) {
metaData, networkData, err = getOpenstackDataFromConfigDrive(mountConfigDrive)
if err != nil {
log.Log.Error(err, "GetOpenStackData(): non-fatal error getting OpenStack data from config drive")
metaData, networkData, err = getOpenstackDataFromMetadataService()
if err != nil {
return metaData, networkData, fmt.Errorf("GetOpenStackData(): error getting OpenStack data: %w", err)
// Attempt to reach MetaData over IPv4 then over IPv6 for both HTTPS and HTTP
reachedMetaData := false
for i, baseURL := range ospBaseURLS {
isIPv6 := i == 1
if isIPv6 {
activeInterface, err := getActiveInterfaceName()
if err != nil {
log.Log.Error(err, "GetOpenStackData(): non-fatal error retrieving active network interface")
continue
}
ospMetaDataBaseURL = constructMetaDataURLs(baseURL, activeInterface, true)
} else {
ospMetaDataBaseURL = constructMetaDataURLs(baseURL, "", false)
}
// Attempt to reach MetaData over HTTPS then over HTTP
for _, baseMetaURL := range ospMetaDataBaseURL {
ospNetworkDataURL = baseMetaURL + "/" + ospNetworkDataJSON
ospMetaDataURL = baseMetaURL + "/" + ospMetaDataJSON
metaData, networkData, err = getOpenstackDataFromMetadataService()
if err == nil {
reachedMetaData = true
break
}
}

if reachedMetaData {
break
}
}
}

if !reachedMetaData {
return metaData, networkData, fmt.Errorf("GetOpenStackData(): error reaching metadata service both over IPv6 and IPv4: %v", err)
}
}
// We can't rely on the PCI address from the metadata so we will lookup the real PCI address
// for the NIC that matches the MAC address.
//
Expand Down