The two problems upstream are that are:
1. cloudinit/distros/networking.py get_interfaces_by_mac doesn't honor blacklist_drivers from a datasource
2. DataSourceAzure sets blacklist_drivers on DataSourceAzure.distro.networking.blacklist_drivers during _get_data.
3. stages.py also does not copy blacklist_drivers into a newly instantiated distro instance on the found datasource.
This will only affect older kernels like 4.4 because any newer kernels surface a sysfs "master" links in SRIOV devices so cloud-init ignores them by default so no duplicate mac errors are seen.
The following diff resolves this for Azure on 4.4 FIPS kernel.
I'll have to talk with the team about how best to support this on Xenial PRO images.
@@ -144,7 +144,9 @@ class Networking(metaclass=abc.ABCMeta): expected_macs = set(expected_ifaces.keys())
# set of current macs
- present_macs = self.get_interfaces_by_mac().keys()
+ present_macs = self.get_interfaces_by_mac(
+ blacklist_drivers=self.blacklist_drivers
+ ).keys()
# compare the set of expected mac address values to
# the current macs present; we only check MAC as cloud-init
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
index dcdf9f8f..0069bd0a 100755
--- a/cloudinit/sources/DataSourceAzure.py
+++ b/cloudinit/sources/DataSourceAzure.py
@@ -344,6 +344,7 @@ class DataSourceAzure(sources.DataSource): EventType.BOOT, EventType.BOOT_LEGACY
}}
+ blacklist_drivers = BLACKLIST_DRIVERS
_negotiated = False
_metadata_imds = sources.UNSET
@@ -626,7 +627,7 @@ class DataSourceAzure(sources.DataSource):
except Exception as e: LOG.warning("Failed to get system information: %s", e)
try: crawled_data = util.log_time(
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index bbded1e9..cc7619b3 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -92,6 +92,14 @@ class Init(object):
# said datasource and move its distro/system config
# from whatever it was to a new set...
if self.datasource is not NULL_DATA_SOURCE:
+ # Certain datasources exclude network devices based
+ # on the corresponding driver (Azure SRIOV).
+ # When copying in a new distro, reset the
+ # blacklist_drivers for networking config generation.
+ if hasattr(self.datasource, "blacklist_drivers"):
+ self._distro.networking.blacklist_drivers = getattr(
+ self.datasource, "blacklist_drivers"
+ ) self.datasource.distro = self._distro self.datasource.sys_cfg = self.cfg
return self._distro
More details, this is a upstream bug due to a cloudinit/stages creating a copy of the distro instance based on re-reading and updating distro config from disk if unset in Init /github. com/canonical/ cloud-init/ blob/master/ cloudinit/ stages. py#L91- L96
https:/
The two problems upstream are that are: distros/ networking. py get_interfaces_ by_mac doesn't honor blacklist_drivers from a datasource .distro. networking. blacklist_ drivers during _get_data.
1. cloudinit/
2. DataSourceAzure sets blacklist_drivers on DataSourceAzure
3. stages.py also does not copy blacklist_drivers into a newly instantiated distro instance on the found datasource.
This will only affect older kernels like 4.4 because any newer kernels surface a sysfs "master" links in SRIOV devices so cloud-init ignores them by default so no duplicate mac errors are seen.
The following diff resolves this for Azure on 4.4 FIPS kernel.
I'll have to talk with the team about how best to support this on Xenial PRO images.
diff --git a/cloudinit/ distros/ networking. py b/cloudinit/ distros/ networking. py distros/ networking. py distros/ networking. py metaclass= abc.ABCMeta) : self) -> list: interfaces( )
index c291196a..471d7e52 100644
--- a/cloudinit/
+++ b/cloudinit/
@@ -71,7 +71,7 @@ class Networking(
def get_interfaces(
return net.get_
- def get_interfaces_ by_mac( self) -> dict: by_mac( self, *, blacklist_ drivers= None) -> dict: interfaces_ by_mac(
blacklist _drivers= self.blacklist_ drivers)
+ def get_interfaces_
return net.get_
@@ -144,7 +144,9 @@ class Networking( metaclass= abc.ABCMeta) :
expected_ macs = set(expected_ ifaces. keys())
# set of current macs interfaces_ by_mac( ).keys( ) interfaces_ by_mac( drivers= self.blacklist_ drivers
- present_macs = self.get_
+ present_macs = self.get_
+ blacklist_
+ ).keys()
# compare the set of expected mac address values to sources/ DataSourceAzure .py b/cloudinit/ sources/ DataSourceAzure .py sources/ DataSourceAzure .py sources/ DataSourceAzure .py (sources. DataSource) :
EventType. BOOT,
EventType. BOOT_LEGACY
# the current macs present; we only check MAC as cloud-init
diff --git a/cloudinit/
index dcdf9f8f..0069bd0a 100755
--- a/cloudinit/
+++ b/cloudinit/
@@ -344,6 +344,7 @@ class DataSourceAzure
}}
+ blacklist_drivers = BLACKLIST_DRIVERS
_negotiated = False (sources. DataSource) :
LOG. warning( "Failed to get system information: %s", e)
_metadata_imds = sources.UNSET
@@ -626,7 +627,7 @@ class DataSourceAzure
except Exception as e:
- self.distro. networking. blacklist_ drivers = BLACKLIST_DRIVERS networking. blacklist_ drivers = self.blacklist_ drivers
+ self.distro.
try:
crawled_ data = util.log_time( stages. py b/cloudinit/ stages. py stages. py stages. py self.datasource , "blacklist_ drivers" ): networking. blacklist_ drivers = getattr(
self. datasource. distro = self._distro
self. datasource. sys_cfg = self.cfg
diff --git a/cloudinit/
index bbded1e9..cc7619b3 100644
--- a/cloudinit/
+++ b/cloudinit/
@@ -92,6 +92,14 @@ class Init(object):
# said datasource and move its distro/system config
# from whatever it was to a new set...
if self.datasource is not NULL_DATA_SOURCE:
+ # Certain datasources exclude network devices based
+ # on the corresponding driver (Azure SRIOV).
+ # When copying in a new distro, reset the
+ # blacklist_drivers for networking config generation.
+ if hasattr(
+ self._distro.
+ self.datasource, "blacklist_drivers"
+ )
return self._distro