Compare commits
No commits in common. 'c10-beta' and 'c9' have entirely different histories.
@ -1 +1 @@
|
||||
387c1d48b72cde444ab13195589deece9d1daa8c SOURCES/24.1.4.tar.gz
|
||||
e73116733f5636eb4bc1a5e47e802c3635b9bfa2 SOURCES/23.4.tar.gz
|
||||
|
@ -1 +1 @@
|
||||
SOURCES/24.1.4.tar.gz
|
||||
SOURCES/23.4.tar.gz
|
||||
|
@ -1,268 +0,0 @@
|
||||
From 8effa259c803f265553b8bfc629835d88815e2dd Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Tue, 26 Mar 2024 15:02:39 -0500
|
||||
Subject: [PATCH] fix(rhel): Fix network ordering in sysconfig
|
||||
|
||||
NM_CONTROLLED=true allows cloud-init to wait until network devices are online.
|
||||
|
||||
See upstream PR: https://github.com/canonical/cloud-init/pull/5089
|
||||
|
||||
Conflicts:
|
||||
tests/unittests/net/network_configs.py
|
||||
This file was added in commit a576d11ef93d ("Cleanup test_net.py (#4840)")
|
||||
This commit is not part of 24.1.2 release.
|
||||
|
||||
(cherry picked from commit 9a7674af70026ba77612c8f53a82573bdc350ff7)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/net/sysconfig.py | 1 -
|
||||
doc/rtd/reference/network-config.rst | 2 --
|
||||
tests/unittests/cmd/devel/test_net_convert.py | 1 -
|
||||
tests/unittests/distros/test_netconfig.py | 8 --------
|
||||
tests/unittests/test_net.py | 16 ----------------
|
||||
5 files changed, 28 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
||||
index 622b8faf..3be35126 100644
|
||||
--- a/cloudinit/net/sysconfig.py
|
||||
+++ b/cloudinit/net/sysconfig.py
|
||||
@@ -317,7 +317,6 @@ class Renderer(renderer.Renderer):
|
||||
"rhel": {
|
||||
"ONBOOT": True,
|
||||
"USERCTL": False,
|
||||
- "NM_CONTROLLED": False,
|
||||
"BOOTPROTO": "none",
|
||||
},
|
||||
"suse": {"BOOTPROTO": "static", "STARTMODE": "auto"},
|
||||
diff --git a/doc/rtd/reference/network-config.rst b/doc/rtd/reference/network-config.rst
|
||||
index d9e67cf7..130b665e 100644
|
||||
--- a/doc/rtd/reference/network-config.rst
|
||||
+++ b/doc/rtd/reference/network-config.rst
|
||||
@@ -308,7 +308,6 @@ Example output:
|
||||
BOOTPROTO=static
|
||||
DEVICE=eth7
|
||||
IPADDR=192.168.1.5/255.255.255.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -316,7 +315,6 @@ Example output:
|
||||
#
|
||||
BOOTPROTO=dhcp
|
||||
DEVICE=eth9
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
diff --git a/tests/unittests/cmd/devel/test_net_convert.py b/tests/unittests/cmd/devel/test_net_convert.py
|
||||
index be2fcdd6..3e9a4f90 100644
|
||||
--- a/tests/unittests/cmd/devel/test_net_convert.py
|
||||
+++ b/tests/unittests/cmd/devel/test_net_convert.py
|
||||
@@ -62,7 +62,6 @@ SAMPLE_SYSCONFIG_CONTENT = """\
|
||||
#
|
||||
BOOTPROTO=dhcp
|
||||
DEVICE=eth0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
diff --git a/tests/unittests/distros/test_netconfig.py b/tests/unittests/distros/test_netconfig.py
|
||||
index d1e251b6..f35e5b0a 100644
|
||||
--- a/tests/unittests/distros/test_netconfig.py
|
||||
+++ b/tests/unittests/distros/test_netconfig.py
|
||||
@@ -723,7 +723,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
GATEWAY=192.168.1.254
|
||||
IPADDR=192.168.1.5
|
||||
NETMASK=255.255.255.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -733,7 +732,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
"""\
|
||||
BOOTPROTO=dhcp
|
||||
DEVICE=eth1
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -764,7 +762,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
IPV6_AUTOCONF=no
|
||||
IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
|
||||
IPV6_FORCE_ACCEPT_RA=no
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -774,7 +771,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
"""\
|
||||
BOOTPROTO=dhcp
|
||||
DEVICE=eth1
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -821,7 +817,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
HWADDR=00:16:3e:60:7c:df
|
||||
IPADDR=192.10.1.2
|
||||
NETMASK=255.255.255.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -833,7 +828,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
DEVICE=infra0
|
||||
IPADDR=10.0.1.2
|
||||
NETMASK=255.255.0.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
PHYSDEV=eth0
|
||||
USERCTL=no
|
||||
@@ -869,7 +863,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
DEVICE=eth0
|
||||
IPADDR=192.10.1.2
|
||||
NETMASK=255.255.255.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -881,7 +874,6 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
|
||||
DEVICE=eth0.1001
|
||||
IPADDR=10.0.1.2
|
||||
NETMASK=255.255.0.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
PHYSDEV=eth0
|
||||
USERCTL=no
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index cb991938..f898543c 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -585,7 +585,6 @@ GATEWAY=172.19.3.254
|
||||
HWADDR=fa:16:3e:ed:9a:59
|
||||
IPADDR=172.19.1.34
|
||||
NETMASK=255.255.252.0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -751,7 +750,6 @@ IPADDR=172.19.1.34
|
||||
IPADDR1=10.0.0.10
|
||||
NETMASK=255.255.252.0
|
||||
NETMASK1=255.255.255.0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -913,7 +911,6 @@ IPV6_AUTOCONF=no
|
||||
IPV6_DEFAULTGW=2001:DB8::1
|
||||
IPV6_FORCE_ACCEPT_RA=no
|
||||
NETMASK=255.255.252.0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5232,7 +5229,6 @@ DEVICE=eth1000
|
||||
DHCPV6C=yes
|
||||
HWADDR=07-1c-c6-75-a4-be
|
||||
IPV6INIT=yes
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5444,7 +5440,6 @@ GATEWAY=10.0.2.2
|
||||
HWADDR=52:54:00:12:34:00
|
||||
IPADDR=10.0.2.15
|
||||
NETMASK=255.255.255.0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5475,7 +5470,6 @@ HWADDR=fa:16:3e:25:b4:59
|
||||
IPADDR=51.68.89.122
|
||||
MTU=1500
|
||||
NETMASK=255.255.240.0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5489,7 +5483,6 @@ DEVICE=eth1
|
||||
DHCLIENT_SET_DEFAULT_ROUTE=no
|
||||
HWADDR=fa:16:3e:b1:ca:29
|
||||
MTU=9000
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5514,7 +5507,6 @@ USERCTL=no
|
||||
#
|
||||
BOOTPROTO=dhcp
|
||||
DEVICE=eth0
|
||||
-NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5761,7 +5753,6 @@ USERCTL=no
|
||||
IPV6_FORCE_ACCEPT_RA=no
|
||||
IPV6_DEFAULTGW=2001:db8::1
|
||||
NETMASK=255.255.255.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5793,7 +5784,6 @@ USERCTL=no
|
||||
"""\
|
||||
BOOTPROTO=none
|
||||
DEVICE=eno1
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5806,7 +5796,6 @@ USERCTL=no
|
||||
IPADDR=192.6.1.9
|
||||
MTU=1495
|
||||
NETMASK=255.255.255.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
PHYSDEV=eno1
|
||||
USERCTL=no
|
||||
@@ -5842,7 +5831,6 @@ USERCTL=no
|
||||
IPADDR=10.101.8.65
|
||||
MTU=1334
|
||||
NETMASK=255.255.255.192
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Bond
|
||||
USERCTL=no
|
||||
@@ -5854,7 +5842,6 @@ USERCTL=no
|
||||
BOOTPROTO=none
|
||||
DEVICE=enp0s0
|
||||
MASTER=bond0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
SLAVE=yes
|
||||
TYPE=Bond
|
||||
@@ -5867,7 +5854,6 @@ USERCTL=no
|
||||
BOOTPROTO=none
|
||||
DEVICE=enp0s1
|
||||
MASTER=bond0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
SLAVE=yes
|
||||
TYPE=Bond
|
||||
@@ -5898,7 +5884,6 @@ USERCTL=no
|
||||
DEVICE=eno1
|
||||
HWADDR=07-1c-c6-75-a4-be
|
||||
METRIC=100
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5989,7 +5974,6 @@ USERCTL=no
|
||||
IPV6_FORCE_ACCEPT_RA=no
|
||||
MTU=1400
|
||||
NETMASK=255.255.248.0
|
||||
- NM_CONTROLLED=no
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
@ -0,0 +1,140 @@
|
||||
From 5fa8113a9efaa90f293b95477c4fa44e3d4b6537 Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Thu, 23 Nov 2023 12:27:51 +0530
|
||||
Subject: [PATCH] net/network_manager: do not set "may-fail" to False for both
|
||||
ipv4 and ipv6 dhcp
|
||||
|
||||
If "may-fail" is set to False in the Network Manager keyfile for both ipv4
|
||||
and ipv6 for dhcp configuration, it essentially means both ipv4 and ipv6 network
|
||||
initialization using dhcp must succeed for the overall network configuration to
|
||||
succeed. This means, for environments where only ipv4 or ipv6 is available but
|
||||
not both and we need to configure both ipv4 and ipv6 dhcp, the overall
|
||||
network configuration will fail. This is not what we want. When both ipv4
|
||||
and ipv6 dhcp are configured, it is enough for the overall configuration to
|
||||
succeed if any one succeeds.
|
||||
Therefore, set "may-fail" to True for both ipv4 and ipv6 if and only if both
|
||||
ipv4 and ipv6 are configured as dhcp in the Network Manager keyfile and
|
||||
"may-fail" is set to False for both. If both ipv4 and ipv6 are configured
|
||||
in the keyfile and if for any of them "may-fail" is already set to True,then
|
||||
do nothing.
|
||||
All other cases remain same as before.
|
||||
|
||||
Please see discussions in PR #4474.
|
||||
|
||||
Co-authored-by: James Falcon <james.falcon@canonical.com>
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
(cherry picked from commit 29dd5ace73ad60c7452c39b840045fb47fe0711f)
|
||||
---
|
||||
cloudinit/net/network_manager.py | 59 ++++++++++++++++++++++++++++++++
|
||||
tests/unittests/test_net.py | 8 ++---
|
||||
2 files changed, 63 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
|
||||
index 8374cfcc..8a99eb3a 100644
|
||||
--- a/cloudinit/net/network_manager.py
|
||||
+++ b/cloudinit/net/network_manager.py
|
||||
@@ -71,6 +71,57 @@ class NMConnection:
|
||||
if not self.config.has_option(section, option):
|
||||
self.config[section][option] = value
|
||||
|
||||
+ def _config_option_is_set(self, section, option):
|
||||
+ """
|
||||
+ Checks if a config option is set. Returns True if it is,
|
||||
+ else returns False.
|
||||
+ """
|
||||
+ return self.config.has_section(section) and self.config.has_option(
|
||||
+ section, option
|
||||
+ )
|
||||
+
|
||||
+ def _get_config_option(self, section, option):
|
||||
+ """
|
||||
+ Returns the value of a config option if its set,
|
||||
+ else returns None.
|
||||
+ """
|
||||
+ if self._config_option_is_set(section, option):
|
||||
+ return self.config[section][option]
|
||||
+ else:
|
||||
+ return None
|
||||
+
|
||||
+ def _change_set_config_option(self, section, option, value):
|
||||
+ """
|
||||
+ Overrides the value of a config option if its already set.
|
||||
+ Else, if the config option is not set, it does nothing.
|
||||
+ """
|
||||
+ if self._config_option_is_set(section, option):
|
||||
+ self.config[section][option] = value
|
||||
+
|
||||
+ def _set_mayfail_true_if_both_false_dhcp(self):
|
||||
+ """
|
||||
+ If for both ipv4 and ipv6, 'may-fail' is set to be False,
|
||||
+ set it to True for both of them.
|
||||
+ """
|
||||
+ for family in ["ipv4", "ipv6"]:
|
||||
+ if self._get_config_option(family, "may-fail") != "false":
|
||||
+ # if either ipv4 or ipv6 sections are not set/configured,
|
||||
+ # or if both are configured but for either ipv4 or ipv6,
|
||||
+ # 'may-fail' is not 'false', do not do anything.
|
||||
+ return
|
||||
+ if self._get_config_option(family, "method") not in [
|
||||
+ "dhcp",
|
||||
+ "auto",
|
||||
+ ]:
|
||||
+ # if both v4 and v6 are not dhcp, do not do anything.
|
||||
+ return
|
||||
+
|
||||
+ # If we landed here, it means both ipv4 and ipv6 are configured
|
||||
+ # with dhcp/auto and both have 'may-fail' set to 'false'. So set
|
||||
+ # both to 'true'.
|
||||
+ for family in ["ipv4", "ipv6"]:
|
||||
+ self._change_set_config_option(family, "may-fail", "true")
|
||||
+
|
||||
def _set_ip_method(self, family, subnet_type):
|
||||
"""
|
||||
Ensures there's appropriate [ipv4]/[ipv6] for given family
|
||||
@@ -271,6 +322,14 @@ class NMConnection:
|
||||
if family == "ipv4" and "mtu" in subnet:
|
||||
ipv4_mtu = subnet["mtu"]
|
||||
|
||||
+ # we do not want to set may-fail to false for both ipv4 and ipv6 dhcp
|
||||
+ # at the at the same time. This will make the network configuration
|
||||
+ # work only when both ipv4 and ipv6 dhcp succeeds. This may not be
|
||||
+ # what we want. If we have configured both ipv4 and ipv6 dhcp, any one
|
||||
+ # succeeding should be enough. Therefore, if "may-fail" is set to
|
||||
+ # False for both ipv4 and ipv6 dhcp, set them both to True.
|
||||
+ self._set_mayfail_true_if_both_false_dhcp()
|
||||
+
|
||||
if ipv4_mtu is None:
|
||||
ipv4_mtu = device_mtu
|
||||
if not ipv4_mtu == device_mtu:
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index cef4fa2d..fb4c863c 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -1477,11 +1477,11 @@ NETWORK_CONFIGS = {
|
||||
|
||||
[ipv4]
|
||||
method=auto
|
||||
- may-fail=false
|
||||
+ may-fail=true
|
||||
|
||||
[ipv6]
|
||||
method=auto
|
||||
- may-fail=false
|
||||
+ may-fail=true
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -1650,11 +1650,11 @@ NETWORK_CONFIGS = {
|
||||
|
||||
[ipv6]
|
||||
method=auto
|
||||
- may-fail=false
|
||||
+ may-fail=true
|
||||
|
||||
[ipv4]
|
||||
method=auto
|
||||
- may-fail=false
|
||||
+ may-fail=true
|
||||
|
||||
"""
|
||||
),
|
@ -1,26 +0,0 @@
|
||||
From 5745b21c9e9e47478b8920e3ccf5e82afffefcdf Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Tue, 26 Mar 2024 15:00:41 -0500
|
||||
Subject: [PATCH] feat: Use NetworkManager renderer by default in RHEL family
|
||||
|
||||
See upstream GH: https://github.com/canonical/cloud-init/pull/5089
|
||||
|
||||
(cherry picked from commit 97537494d95f733fce260beb5a2d465cccf9f6c7)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
config/cloud.cfg.tmpl | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
|
||||
index 0e785dbe..a4701209 100644
|
||||
--- a/config/cloud.cfg.tmpl
|
||||
+++ b/config/cloud.cfg.tmpl
|
||||
@@ -306,7 +306,7 @@ system_info:
|
||||
activators: ['netplan', 'eni', 'network-manager', 'networkd']
|
||||
{% elif is_rhel %}
|
||||
network:
|
||||
- renderers: ['sysconfig', 'eni', 'netplan', 'network-manager', 'networkd']
|
||||
+ renderers: ['eni', 'netplan', 'network-manager', 'sysconfig', 'networkd']
|
||||
{% endif %}
|
||||
{% if variant == "photon" %}
|
||||
# If set to true, cloud-init will not use fallback network config.
|
@ -0,0 +1,172 @@
|
||||
From 54e87eaad7841270e530beff2dcfe68292ae87ef Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Tue, 21 Nov 2023 13:57:15 +0530
|
||||
Subject: [PATCH] net: allow dhcp6 configuration from
|
||||
generate_fallback_configuration()
|
||||
|
||||
This will make sure on Azure we can use both dhcp4 and dhcp6 when IMDS is not
|
||||
used. This is useful in situations where only ipv6 network is available and
|
||||
there is no dhcp4 running.
|
||||
|
||||
This change is mostly a reversal of commit 29ed5f5b646ee and therefore,
|
||||
re-application of the commit 518047aea9 with some small changes.
|
||||
|
||||
The issue that caused the reversal of 518047aea9 is fixed by the earlier commit:
|
||||
cab0eaf290af7 ("net/network_manager: do not set "may-fail" to False for both ipv4 and ipv6 dhcp")
|
||||
|
||||
Fixes GH-4439
|
||||
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
(cherry picked from commit 0264e969166846b2f5cf87ccdb051a3a795eca15)
|
||||
---
|
||||
cloudinit/net/__init__.py | 7 ++++++-
|
||||
tests/unittests/net/test_init.py | 4 ++++
|
||||
tests/unittests/test_net.py | 24 +++++++++++++++++++++---
|
||||
3 files changed, 31 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
|
||||
index bf21633b..c0888f52 100644
|
||||
--- a/cloudinit/net/__init__.py
|
||||
+++ b/cloudinit/net/__init__.py
|
||||
@@ -571,7 +571,12 @@ def generate_fallback_config(config_driver=None):
|
||||
match = {
|
||||
"macaddress": read_sys_net_safe(target_name, "address").lower()
|
||||
}
|
||||
- cfg = {"dhcp4": True, "set-name": target_name, "match": match}
|
||||
+ cfg = {
|
||||
+ "dhcp4": True,
|
||||
+ "dhcp6": True,
|
||||
+ "set-name": target_name,
|
||||
+ "match": match,
|
||||
+ }
|
||||
if config_driver:
|
||||
driver = device_driver(target_name)
|
||||
if driver:
|
||||
diff --git a/tests/unittests/net/test_init.py b/tests/unittests/net/test_init.py
|
||||
index 561d5151..60a44186 100644
|
||||
--- a/tests/unittests/net/test_init.py
|
||||
+++ b/tests/unittests/net/test_init.py
|
||||
@@ -261,6 +261,7 @@ class TestGenerateFallbackConfig(CiTestCase):
|
||||
"eth1": {
|
||||
"match": {"macaddress": mac},
|
||||
"dhcp4": True,
|
||||
+ "dhcp6": True,
|
||||
"set-name": "eth1",
|
||||
}
|
||||
},
|
||||
@@ -278,6 +279,7 @@ class TestGenerateFallbackConfig(CiTestCase):
|
||||
"eth0": {
|
||||
"match": {"macaddress": mac},
|
||||
"dhcp4": True,
|
||||
+ "dhcp6": True,
|
||||
"set-name": "eth0",
|
||||
}
|
||||
},
|
||||
@@ -293,6 +295,7 @@ class TestGenerateFallbackConfig(CiTestCase):
|
||||
"ethernets": {
|
||||
"eth0": {
|
||||
"dhcp4": True,
|
||||
+ "dhcp6": True,
|
||||
"match": {"macaddress": mac},
|
||||
"set-name": "eth0",
|
||||
}
|
||||
@@ -359,6 +362,7 @@ class TestGenerateFallbackConfig(CiTestCase):
|
||||
"ethernets": {
|
||||
"ens3": {
|
||||
"dhcp4": True,
|
||||
+ "dhcp6": True,
|
||||
"match": {"name": "ens3"},
|
||||
"set-name": "ens3",
|
||||
}
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index fb4c863c..d9ef493b 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -4339,6 +4339,7 @@ class TestGenerateFallbackConfig(CiTestCase):
|
||||
"ethernets": {
|
||||
"eth0": {
|
||||
"dhcp4": True,
|
||||
+ "dhcp6": True,
|
||||
"set-name": "eth0",
|
||||
"match": {
|
||||
"macaddress": "00:11:22:33:44:55",
|
||||
@@ -4423,6 +4424,9 @@ iface lo inet loopback
|
||||
|
||||
auto eth0
|
||||
iface eth0 inet dhcp
|
||||
+
|
||||
+# control-alias eth0
|
||||
+iface eth0 inet6 dhcp
|
||||
"""
|
||||
self.assertEqual(expected.lstrip(), contents.lstrip())
|
||||
|
||||
@@ -4512,6 +4516,9 @@ iface lo inet loopback
|
||||
|
||||
auto eth1
|
||||
iface eth1 inet dhcp
|
||||
+
|
||||
+# control-alias eth1
|
||||
+iface eth1 inet6 dhcp
|
||||
"""
|
||||
self.assertEqual(expected.lstrip(), contents.lstrip())
|
||||
|
||||
@@ -4736,7 +4743,9 @@ class TestRhelSysConfigRendering(CiTestCase):
|
||||
AUTOCONNECT_PRIORITY=120
|
||||
BOOTPROTO=dhcp
|
||||
DEVICE=eth1000
|
||||
+DHCPV6C=yes
|
||||
HWADDR=07-1c-c6-75-a4-be
|
||||
+IPV6INIT=yes
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no
|
||||
@@ -5646,7 +5655,8 @@ class TestOpenSuseSysConfigRendering(CiTestCase):
|
||||
expected_content = """
|
||||
# Created by cloud-init automatically, do not edit.
|
||||
#
|
||||
-BOOTPROTO=dhcp4
|
||||
+BOOTPROTO=dhcp
|
||||
+DHCLIENT6_MODE=managed
|
||||
LLADDR=07-1c-c6-75-a4-be
|
||||
STARTMODE=auto
|
||||
""".lstrip()
|
||||
@@ -6032,7 +6042,11 @@ class TestNetworkManagerRendering(CiTestCase):
|
||||
|
||||
[ipv4]
|
||||
method=auto
|
||||
- may-fail=false
|
||||
+ may-fail=true
|
||||
+
|
||||
+ [ipv6]
|
||||
+ method=auto
|
||||
+ may-fail=true
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -6298,6 +6312,9 @@ iface lo inet loopback
|
||||
|
||||
auto eth1000
|
||||
iface eth1000 inet dhcp
|
||||
+
|
||||
+# control-alias eth1000
|
||||
+iface eth1000 inet6 dhcp
|
||||
"""
|
||||
self.assertEqual(expected.lstrip(), contents.lstrip())
|
||||
|
||||
@@ -6357,6 +6374,7 @@ class TestNetplanNetRendering:
|
||||
ethernets:
|
||||
eth1000:
|
||||
dhcp4: true
|
||||
+ dhcp6: true
|
||||
match:
|
||||
macaddress: 07-1c-c6-75-a4-be
|
||||
set-name: eth1000
|
||||
@@ -7856,7 +7874,7 @@ class TestNetworkdNetRendering(CiTestCase):
|
||||
Name=eth1000
|
||||
MACAddress=07-1c-c6-75-a4-be
|
||||
[Network]
|
||||
- DHCP=ipv4"""
|
||||
+ DHCP=yes"""
|
||||
).rstrip(" ")
|
||||
|
||||
expected = self.create_conf_dict(expected.splitlines())
|
@ -0,0 +1,113 @@
|
||||
From c0df864e373e1e34bf23c4869acdf7d20aea7aaf Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Thu, 7 Dec 2023 02:39:51 +0530
|
||||
Subject: [PATCH] net/nm: check for presence of ifcfg files when nm connection
|
||||
files are absent (#4645)
|
||||
|
||||
On systems that use network manager to manage connections and activate network
|
||||
interfaces, they may also use ifcfg files for configuring
|
||||
interfaces using ifcfg-rh network manager plugin. When network manager is used
|
||||
as the activator, we need to also check for the presence of ifcfg interface
|
||||
config file when the network manager connection file is absent and if ifcfg-rh
|
||||
plugin is present.
|
||||
Hence, with this change, network manager activator first tries to use network
|
||||
manager connection files to bring up or bring down the interface. If the
|
||||
connection files are not present and if ifcfg-rh plugin is present, it tries to
|
||||
use ifcfg files for the interface. If the plugin or the ifcfg files are not
|
||||
present, the activator fails to activate or deactivate the interface and it
|
||||
bails out with warning log.
|
||||
|
||||
Fixes: GH-4640
|
||||
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
(cherry picked from commit d1d5166895da471cff3606c70d4e8ab6eec1c006)
|
||||
---
|
||||
cloudinit/net/activators.py | 7 +++++++
|
||||
cloudinit/net/network_manager.py | 33 ++++++++++++++++++++++++++++++--
|
||||
2 files changed, 38 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/activators.py b/cloudinit/net/activators.py
|
||||
index e69da40d..dd858862 100644
|
||||
--- a/cloudinit/net/activators.py
|
||||
+++ b/cloudinit/net/activators.py
|
||||
@@ -117,6 +117,13 @@ class NetworkManagerActivator(NetworkActivator):
|
||||
from cloudinit.net.network_manager import conn_filename
|
||||
|
||||
filename = conn_filename(device_name)
|
||||
+ if filename is None:
|
||||
+ LOG.warning(
|
||||
+ "Unable to find an interface config file. "
|
||||
+ "Unable to bring up interface."
|
||||
+ )
|
||||
+ return False
|
||||
+
|
||||
cmd = ["nmcli", "connection", "load", filename]
|
||||
if _alter_interface(cmd, device_name):
|
||||
cmd = ["nmcli", "connection", "up", "filename", filename]
|
||||
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
|
||||
index 8a99eb3a..76a0ac15 100644
|
||||
--- a/cloudinit/net/network_manager.py
|
||||
+++ b/cloudinit/net/network_manager.py
|
||||
@@ -17,10 +17,12 @@ from typing import Optional
|
||||
from cloudinit import subp, util
|
||||
from cloudinit.net import is_ipv6_address, renderer, subnet_is_ipv6
|
||||
from cloudinit.net.network_state import NetworkState
|
||||
+from cloudinit.net.sysconfig import available_nm_ifcfg_rh
|
||||
|
||||
NM_RUN_DIR = "/etc/NetworkManager"
|
||||
NM_LIB_DIR = "/usr/lib/NetworkManager"
|
||||
NM_CFG_FILE = "/etc/NetworkManager/NetworkManager.conf"
|
||||
+IFCFG_CFG_FILE = "/etc/sysconfig/network-scripts"
|
||||
NM_IPV6_ADDR_GEN_CONF = """# This is generated by cloud-init. Do not edit.
|
||||
#
|
||||
[.config]
|
||||
@@ -442,7 +444,7 @@ class Renderer(renderer.Renderer):
|
||||
for con_id, conn in self.connections.items():
|
||||
if not conn.valid():
|
||||
continue
|
||||
- name = conn_filename(con_id, target)
|
||||
+ name = nm_conn_filename(con_id, target)
|
||||
util.write_file(name, conn.dump(), 0o600)
|
||||
|
||||
# Select EUI64 to be used by default by NM for creating the address
|
||||
@@ -452,12 +454,39 @@ class Renderer(renderer.Renderer):
|
||||
)
|
||||
|
||||
|
||||
-def conn_filename(con_id, target=None):
|
||||
+def nm_conn_filename(con_id, target=None):
|
||||
target_con_dir = subp.target_path(target, NM_RUN_DIR)
|
||||
con_file = f"cloud-init-{con_id}.nmconnection"
|
||||
return f"{target_con_dir}/system-connections/{con_file}"
|
||||
|
||||
|
||||
+def sysconfig_conn_filename(devname, target=None):
|
||||
+ target_con_dir = subp.target_path(target, IFCFG_CFG_FILE)
|
||||
+ con_file = f"ifcfg-{devname}"
|
||||
+ return f"{target_con_dir}/{con_file}"
|
||||
+
|
||||
+
|
||||
+def conn_filename(devname):
|
||||
+ """
|
||||
+ This function returns the name of the interface config file.
|
||||
+ It first checks for presence of network manager connection file.
|
||||
+ If absent and ifcfg-rh plugin for network manager is available,
|
||||
+ it returns the name of the ifcfg file if it is present. If the
|
||||
+ plugin is not present or the plugin is present but ifcfg file is
|
||||
+ not, it returns None.
|
||||
+ This function is called from NetworkManagerActivator class in
|
||||
+ activators.py.
|
||||
+ """
|
||||
+ conn_file = nm_conn_filename(devname)
|
||||
+ # If the network manager connection file is absent, also check for
|
||||
+ # presence of ifcfg files for the same interface (if nm-ifcfg-rh plugin is
|
||||
+ # present, network manager can handle ifcfg files). If both network manager
|
||||
+ # connection file and ifcfg files are absent, return None.
|
||||
+ if not os.path.isfile(conn_file) and available_nm_ifcfg_rh():
|
||||
+ conn_file = sysconfig_conn_filename(devname)
|
||||
+ return conn_file if os.path.isfile(conn_file) else None
|
||||
+
|
||||
+
|
||||
def cloud_init_nm_conf_filename(target=None):
|
||||
target_con_dir = subp.target_path(target, NM_RUN_DIR)
|
||||
conf_file = "30-cloud-init-ip6-addr-gen-mode.conf"
|
@ -0,0 +1,38 @@
|
||||
From e5258b60a3dbf44ef1faac91db2b45dab09de0b5 Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Tue, 16 Jan 2024 12:43:17 -0700
|
||||
Subject: [PATCH] test(jsonschema): Pin jsonschema version (#4781)
|
||||
|
||||
Release 4.21.0 broke tests
|
||||
|
||||
(cherry picked from commit 034a5cdf10582da0492321f861b2b8b42182a54e)
|
||||
---
|
||||
requirements.txt | 2 +-
|
||||
test-requirements.txt | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/requirements.txt b/requirements.txt
|
||||
index edec46a7..a095de18 100644
|
||||
--- a/requirements.txt
|
||||
+++ b/requirements.txt
|
||||
@@ -28,7 +28,7 @@ requests
|
||||
jsonpatch
|
||||
|
||||
# For validating cloud-config sections per schema definitions
|
||||
-jsonschema
|
||||
+jsonschema<=4.20.0
|
||||
|
||||
# Used by DataSourceVMware to inspect the host's network configuration during
|
||||
# the "setup()" function.
|
||||
diff --git a/test-requirements.txt b/test-requirements.txt
|
||||
index 19488b94..46a98b4c 100644
|
||||
--- a/test-requirements.txt
|
||||
+++ b/test-requirements.txt
|
||||
@@ -9,6 +9,6 @@ pytest!=7.3.2
|
||||
pytest-cov
|
||||
pytest-mock
|
||||
setuptools
|
||||
-jsonschema
|
||||
+jsonschema<=4.20.0
|
||||
responses
|
||||
passlib
|
@ -0,0 +1,121 @@
|
||||
From 9e8fbb736d5e8db8bcf0fbc35a76bdad9251990a Mon Sep 17 00:00:00 2001
|
||||
From: d1r3ct0r <calvin.mwadime@canonical.com>
|
||||
Date: Sat, 20 Jan 2024 02:11:47 +0300
|
||||
Subject: [PATCH] fix(clean): stop warning when running clean command (#4761)
|
||||
|
||||
When the clean command is run, runparts is called and README in
|
||||
/etc/cloud/clean.d is not executable which leads to a warning.
|
||||
|
||||
No longer deliver the README in our deb package, move content
|
||||
to our online docs. Continue to deliver the /etc/cloud/clean.d
|
||||
directory as it is used by installers like subiquity.
|
||||
|
||||
Fixes: GH-4760
|
||||
(cherry picked from commit da08a260965e35fa63def1cd8b8b472f7c354ffe)
|
||||
|
||||
There is a downstream only change that is squashed with the upstream commit.
|
||||
The spec file under .distro/ has been updated so as to not include
|
||||
/etc/cloud/clean.d/README file. Otherwise, we shall see errors like the
|
||||
following during the build process:
|
||||
|
||||
error: File not found: /builddir/build/.../etc/cloud/clean.d/README
|
||||
|
||||
After a rebase, we can only maintain the downstream spec file change as
|
||||
the rest of it is clean cherry-pick from upstream.
|
||||
|
||||
X-downstream-only: true
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
config/clean.d/README | 18 ------------------
|
||||
doc/rtd/reference/cli.rst | 27 +++++++++++++++++++++++++++
|
||||
packages/redhat/cloud-init.spec.in | 1 -
|
||||
packages/suse/cloud-init.spec.in | 1 -
|
||||
4 files changed, 27 insertions(+), 20 deletions(-)
|
||||
delete mode 100644 config/clean.d/README
|
||||
|
||||
diff --git a/config/clean.d/README b/config/clean.d/README
|
||||
deleted file mode 100644
|
||||
index 9b0feebe..00000000
|
||||
--- a/config/clean.d/README
|
||||
+++ /dev/null
|
||||
@@ -1,18 +0,0 @@
|
||||
--- cloud-init's clean.d run-parts directory --
|
||||
-
|
||||
-This directory is provided for third party applications which need
|
||||
-additional configuration artifact cleanup from the filesystem when
|
||||
-the command `cloud-init clean` is invoked.
|
||||
-
|
||||
-The `cloud-init clean` operation is typically performed by image creators
|
||||
-when preparing a golden image for clone and redeployment. The clean command
|
||||
-removes any cloud-init semaphores, allowing cloud-init to treat the next
|
||||
-boot of this image as the "first boot". When the image is next booted
|
||||
-cloud-init will performing all initial configuration based on any valid
|
||||
-datasource meta-data and user-data.
|
||||
-
|
||||
-Any executable scripts in this subdirectory will be invoked in lexicographical
|
||||
-order with run-parts by the command: sudo cloud-init clean.
|
||||
-
|
||||
-Typical format of such scripts would be a ##-<some-app> like the following:
|
||||
- /etc/cloud/clean.d/99-live-installer
|
||||
diff --git a/doc/rtd/reference/cli.rst b/doc/rtd/reference/cli.rst
|
||||
index 04e05c55..c36775a8 100644
|
||||
--- a/doc/rtd/reference/cli.rst
|
||||
+++ b/doc/rtd/reference/cli.rst
|
||||
@@ -83,6 +83,33 @@ re-run all stages as it did on first boot.
|
||||
config files for ssh daemon. Argument `network` removes all generated
|
||||
config files for network. `all` removes config files of all types.
|
||||
|
||||
+.. note::
|
||||
+
|
||||
+ Cloud-init provides the directory :file:`/etc/cloud/clean.d/` for third party
|
||||
+ applications which need additional configuration artifact cleanup from
|
||||
+ the fileystem when the `clean` command is invoked.
|
||||
+
|
||||
+ The :command:`clean` operation is typically performed by image creators
|
||||
+ when preparing a golden image for clone and redeployment. The clean command
|
||||
+ removes any cloud-init semaphores, allowing cloud-init to treat the next
|
||||
+ boot of this image as the "first boot". When the image is next booted
|
||||
+ cloud-init will performing all initial configuration based on any valid
|
||||
+ datasource meta-data and user-data.
|
||||
+
|
||||
+ Any executable scripts in this subdirectory will be invoked in lexicographical
|
||||
+ order with run-parts when running the :command:`clean` command.
|
||||
+
|
||||
+ Typical format of such scripts would be a ##-<some-app> like the following:
|
||||
+ :file:`/etc/cloud/clean.d/99-live-installer`
|
||||
+
|
||||
+ An example of a script is:
|
||||
+
|
||||
+ .. code-block:: bash
|
||||
+
|
||||
+ sudo rm -rf /var/lib/installer_imgs/
|
||||
+ sudo rm -rf /var/log/installer/
|
||||
+
|
||||
+
|
||||
.. _cli_collect_logs:
|
||||
|
||||
:command:`collect-logs`
|
||||
diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in
|
||||
index 97e95096..accfb1b6 100644
|
||||
--- a/packages/redhat/cloud-init.spec.in
|
||||
+++ b/packages/redhat/cloud-init.spec.in
|
||||
@@ -190,7 +190,6 @@ fi
|
||||
# Configs
|
||||
%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg
|
||||
%dir %{_sysconfdir}/cloud/clean.d
|
||||
-%config(noreplace) %{_sysconfdir}/cloud/clean.d/README
|
||||
%dir %{_sysconfdir}/cloud/cloud.cfg.d
|
||||
%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/*.cfg
|
||||
%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/README
|
||||
diff --git a/packages/suse/cloud-init.spec.in b/packages/suse/cloud-init.spec.in
|
||||
index 62a9129b..fae3c12b 100644
|
||||
--- a/packages/suse/cloud-init.spec.in
|
||||
+++ b/packages/suse/cloud-init.spec.in
|
||||
@@ -115,7 +115,6 @@ version_pys=$(cd "%{buildroot}" && find . -name version.py -type f)
|
||||
|
||||
# Configs
|
||||
%dir %{_sysconfdir}/cloud/clean.d
|
||||
-%config(noreplace) %{_sysconfdir}/cloud/clean.d/README
|
||||
%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg
|
||||
%dir %{_sysconfdir}/cloud/cloud.cfg.d
|
||||
%config(noreplace) %{_sysconfdir}/cloud/cloud.cfg.d/*.cfg
|
@ -1,176 +0,0 @@
|
||||
From 86930b77ad18aa8dbad8908ddad8852447db0242 Mon Sep 17 00:00:00 2001
|
||||
From: PengpengSun <40026211+PengpengSun@users.noreply.github.com>
|
||||
Date: Tue, 12 Mar 2024 09:26:55 +0800
|
||||
Subject: [PATCH 3/3] DS VMware: Fix ipv6 addr converter from netinfo to
|
||||
netifaces (#5029)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 80: refactor: remove dependency on netifaces (#4634)
|
||||
RH-Jira: RHEL-34518
|
||||
RH-Acked-by: xiachen <xiachen@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [2/2] d718522dec4c77dd153fb239d2d0a6bd9ef581a2 (anisinha/cloud-init)
|
||||
|
||||
Found an issue when verifying PR 4634 on vSphere platform,
|
||||
which is failing to convert ipv6 addr from netinfo format to
|
||||
netifaces format due to 'bcast' is not existing in ipv6.
|
||||
|
||||
This PR is fixing this by updating ipv4 converter function and
|
||||
adding a new ipv6 converter function, also adding unit tests.
|
||||
|
||||
(cherry picked from commit e544a0db82caee17ee19465f8689ce02e564286f)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/sources/DataSourceVMware.py | 45 ++++++++++++++++++++------
|
||||
tests/unittests/sources/test_vmware.py | 33 +++++++++++++++++++
|
||||
2 files changed, 68 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/sources/DataSourceVMware.py b/cloudinit/sources/DataSourceVMware.py
|
||||
index 6ed6a6a5..888060c9 100644
|
||||
--- a/cloudinit/sources/DataSourceVMware.py
|
||||
+++ b/cloudinit/sources/DataSourceVMware.py
|
||||
@@ -859,19 +859,18 @@ def is_valid_ip_addr(val):
|
||||
)
|
||||
|
||||
|
||||
-def convert_to_netifaces_format(addr):
|
||||
+def convert_to_netifaces_ipv4_format(addr: dict) -> dict:
|
||||
"""
|
||||
Takes a cloudinit.netinfo formatted address and converts to netifaces
|
||||
format, since this module was originally written with netifaces as the
|
||||
network introspection module.
|
||||
- netifaces format:
|
||||
+ netifaces ipv4 format:
|
||||
{
|
||||
"broadcast": "10.15.255.255",
|
||||
"netmask": "255.240.0.0",
|
||||
"addr": "10.0.1.4"
|
||||
}
|
||||
-
|
||||
- cloudinit.netinfo format:
|
||||
+ cloudinit.netinfo ipv4 format:
|
||||
{
|
||||
"ip": "10.0.1.4",
|
||||
"mask": "255.240.0.0",
|
||||
@@ -879,10 +878,37 @@ def convert_to_netifaces_format(addr):
|
||||
"scope": "global",
|
||||
}
|
||||
"""
|
||||
+ if not addr.get("ip"):
|
||||
+ return {}
|
||||
+ return {
|
||||
+ "broadcast": addr.get("bcast"),
|
||||
+ "netmask": addr.get("mask"),
|
||||
+ "addr": addr.get("ip"),
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+def convert_to_netifaces_ipv6_format(addr: dict) -> dict:
|
||||
+ """
|
||||
+ Takes a cloudinit.netinfo formatted address and converts to netifaces
|
||||
+ format, since this module was originally written with netifaces as the
|
||||
+ network introspection module.
|
||||
+ netifaces ipv6 format:
|
||||
+ {
|
||||
+ "netmask": "ffff:ffff:ffff:ffff::/64",
|
||||
+ "addr": "2001:db8:abcd:1234::1"
|
||||
+ }
|
||||
+ cloudinit.netinfo ipv6 format:
|
||||
+ {
|
||||
+ "ip": "2001:db8:abcd:1234::1/64",
|
||||
+ "scope6": "global",
|
||||
+ }
|
||||
+ """
|
||||
+ if not addr.get("ip"):
|
||||
+ return {}
|
||||
+ ipv6 = ipaddress.IPv6Interface(addr.get("ip"))
|
||||
return {
|
||||
- "broadcast": addr["bcast"],
|
||||
- "netmask": addr["mask"],
|
||||
- "addr": addr["ip"],
|
||||
+ "netmask": f"{ipv6.netmask}/{ipv6.network.prefixlen}",
|
||||
+ "addr": str(ipv6.ip),
|
||||
}
|
||||
|
||||
|
||||
@@ -890,7 +916,6 @@ def get_host_info():
|
||||
"""
|
||||
Returns host information such as the host name and network interfaces.
|
||||
"""
|
||||
- # TODO(look to promote netifices use up in cloud-init netinfo funcs)
|
||||
host_info = {
|
||||
"network": {
|
||||
"interfaces": {
|
||||
@@ -921,9 +946,9 @@ def get_host_info():
|
||||
af_inet4 = []
|
||||
af_inet6 = []
|
||||
for addr in ifaces[dev_name]["ipv4"]:
|
||||
- af_inet4.append(convert_to_netifaces_format(addr))
|
||||
+ af_inet4.append(convert_to_netifaces_ipv4_format(addr))
|
||||
for addr in ifaces[dev_name]["ipv6"]:
|
||||
- af_inet6.append(convert_to_netifaces_format(addr))
|
||||
+ af_inet6.append(convert_to_netifaces_ipv6_format(addr))
|
||||
|
||||
mac = ifaces[dev_name].get("hwaddr")
|
||||
|
||||
diff --git a/tests/unittests/sources/test_vmware.py b/tests/unittests/sources/test_vmware.py
|
||||
index 33193f89..cfeff6d5 100644
|
||||
--- a/tests/unittests/sources/test_vmware.py
|
||||
+++ b/tests/unittests/sources/test_vmware.py
|
||||
@@ -77,6 +77,11 @@ VMW_IPV4_NETDEV_ADDR = {
|
||||
"mask": "255.255.255.0",
|
||||
"scope": "global",
|
||||
}
|
||||
+VMW_IPV4_NETIFACES_ADDR = {
|
||||
+ "broadcast": "10.85.130.255",
|
||||
+ "netmask": "255.255.255.0",
|
||||
+ "addr": "10.85.130.116",
|
||||
+}
|
||||
VMW_IPV6_ROUTEINFO = {
|
||||
"destination": "::/0",
|
||||
"flags": "UG",
|
||||
@@ -88,6 +93,18 @@ VMW_IPV6_NETDEV_ADDR = {
|
||||
"ip": "fd42:baa2:3dd:17a:216:3eff:fe16:db54/64",
|
||||
"scope6": "global",
|
||||
}
|
||||
+VMW_IPV6_NETIFACES_ADDR = {
|
||||
+ "netmask": "ffff:ffff:ffff:ffff::/64",
|
||||
+ "addr": "fd42:baa2:3dd:17a:216:3eff:fe16:db54",
|
||||
+}
|
||||
+VMW_IPV6_NETDEV_PEER_ADDR = {
|
||||
+ "ip": "fd42:baa2:3dd:17a:216:3eff:fe16:db54",
|
||||
+ "scope6": "global",
|
||||
+}
|
||||
+VMW_IPV6_NETIFACES_PEER_ADDR = {
|
||||
+ "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128",
|
||||
+ "addr": "fd42:baa2:3dd:17a:216:3eff:fe16:db54",
|
||||
+}
|
||||
|
||||
|
||||
def generate_test_netdev_data(ipv4=None, ipv6=None):
|
||||
@@ -147,6 +164,22 @@ class TestDataSourceVMware(CiTestCase):
|
||||
ret = ds.get_data()
|
||||
self.assertFalse(ret)
|
||||
|
||||
+ def test_convert_to_netifaces_ipv4_format(self):
|
||||
+ netifaces_format = DataSourceVMware.convert_to_netifaces_ipv4_format(
|
||||
+ VMW_IPV4_NETDEV_ADDR
|
||||
+ )
|
||||
+ self.assertEqual(netifaces_format, VMW_IPV4_NETIFACES_ADDR)
|
||||
+
|
||||
+ def test_convert_to_netifaces_ipv6_format(self):
|
||||
+ netifaces_format = DataSourceVMware.convert_to_netifaces_ipv6_format(
|
||||
+ VMW_IPV6_NETDEV_ADDR
|
||||
+ )
|
||||
+ self.assertEqual(netifaces_format, VMW_IPV6_NETIFACES_ADDR)
|
||||
+ netifaces_format = DataSourceVMware.convert_to_netifaces_ipv6_format(
|
||||
+ VMW_IPV6_NETDEV_PEER_ADDR
|
||||
+ )
|
||||
+ self.assertEqual(netifaces_format, VMW_IPV6_NETIFACES_PEER_ADDR)
|
||||
+
|
||||
@mock.patch("cloudinit.sources.DataSourceVMware.get_default_ip_addrs")
|
||||
def test_get_host_info_ipv4(self, m_fn_ipaddr):
|
||||
m_fn_ipaddr.return_value = ("10.10.10.1", None)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,145 @@
|
||||
From 94c0cd9c656877250f7e5cfe05325a42bbdec182 Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Fri, 4 Oct 2024 02:38:23 +0530
|
||||
Subject: [PATCH 1/2] Fix metric setting for ifcfg network connections for rhel
|
||||
(#5777)
|
||||
|
||||
RH-Author: xiachen <xiachen@redhat.com>
|
||||
RH-MergeRequest: 145: Fix metric setting for ifcfg network connections for rhel (#5777)
|
||||
RH-Jira: RHEL-65018
|
||||
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
|
||||
RH-Commit: [1/2] a18205f1ffa455a2ccd836ba6baa12b7da0afbde (xiachen/cloud-init)
|
||||
|
||||
Most RHEL systems use Network manager to bring up manage network connections.
|
||||
Network manager does not recognize "METRIC" option for network connections.
|
||||
It uses IPV4_ROUTE_METRIC and IPV6_ROUTE_METRIC options. Please see
|
||||
https://people.freedesktop.org/~lkundrak/nm-docs/nm-settings-ifcfg-rh.html
|
||||
|
||||
This change ensures that cloud-init generates ifcfg network connection files
|
||||
with IPV{4/6}_ROUTE_METRIC options that are compatible with RHEL and
|
||||
network manager.
|
||||
|
||||
Fixes GH-5776
|
||||
|
||||
(cherry picked from commit a399f4b0815234e3fe11255178c737902b2d243d)
|
||||
|
||||
Signed-off-by: Amy Chen <xiachen@redhat.com>
|
||||
---
|
||||
cloudinit/net/sysconfig.py | 21 ++++++++++++++++++---
|
||||
tests/unittests/test_net.py | 37 ++++++++++++++++++++-----------------
|
||||
2 files changed, 38 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
||||
index 7eb430ed1..b50a6a8a0 100644
|
||||
--- a/cloudinit/net/sysconfig.py
|
||||
+++ b/cloudinit/net/sysconfig.py
|
||||
@@ -205,7 +205,7 @@ class Route(ConfigMap):
|
||||
)
|
||||
metric_key = "METRIC" + index
|
||||
if metric_key in self._conf:
|
||||
- metric_value = str(self._conf["METRIC" + index])
|
||||
+ metric_value = str(self._conf[metric_key])
|
||||
buf.write(
|
||||
"%s=%s\n"
|
||||
% ("METRIC" + str(reindex), _quote_value(metric_value))
|
||||
@@ -549,7 +549,12 @@ class Renderer(renderer.Renderer):
|
||||
subnet_type = subnet.get("type")
|
||||
# metric may apply to both dhcp and static config
|
||||
if "metric" in subnet:
|
||||
- if flavor != "suse":
|
||||
+ if flavor == "rhel":
|
||||
+ if subnet_is_ipv6(subnet):
|
||||
+ iface_cfg["IPV6_ROUTE_METRIC"] = subnet["metric"]
|
||||
+ else:
|
||||
+ iface_cfg["IPV4_ROUTE_METRIC"] = subnet["metric"]
|
||||
+ elif flavor != "suse":
|
||||
iface_cfg["METRIC"] = subnet["metric"]
|
||||
if subnet_type in ["dhcp", "dhcp4"]:
|
||||
# On SUSE distros 'DHCLIENT_SET_DEFAULT_ROUTE' is a global
|
||||
@@ -656,7 +661,17 @@ class Renderer(renderer.Renderer):
|
||||
iface_cfg["GATEWAY"] = route["gateway"]
|
||||
route_cfg.has_set_default_ipv4 = True
|
||||
if "metric" in route:
|
||||
- iface_cfg["METRIC"] = route["metric"]
|
||||
+ if flavor == "rhel":
|
||||
+ if subnet_is_ipv6(subnet):
|
||||
+ iface_cfg["IPV6_ROUTE_METRIC"] = route[
|
||||
+ "metric"
|
||||
+ ]
|
||||
+ else:
|
||||
+ iface_cfg["IPV4_ROUTE_METRIC"] = route[
|
||||
+ "metric"
|
||||
+ ]
|
||||
+ else:
|
||||
+ iface_cfg["METRIC"] = route["metric"]
|
||||
|
||||
else:
|
||||
# add default routes only to ifcfg files, not
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index 2d716f4b5..4673e4eaf 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -1345,7 +1345,7 @@ NETWORK_CONFIGS = {
|
||||
HWADDR=c0:d6:9f:2c:e8:80
|
||||
IPADDR=192.168.21.3
|
||||
NETMASK=255.255.255.0
|
||||
- METRIC=10000
|
||||
+ IPV4_ROUTE_METRIC=10000
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no"""
|
||||
@@ -1521,7 +1521,7 @@ NETWORK_CONFIGS = {
|
||||
HWADDR=c0:d6:9f:2c:e8:80
|
||||
IPADDR=192.168.21.3
|
||||
NETMASK=255.255.255.0
|
||||
- METRIC=10000
|
||||
+ IPV4_ROUTE_METRIC=10000
|
||||
ONBOOT=yes
|
||||
TYPE=Ethernet
|
||||
USERCTL=no"""
|
||||
@@ -5725,24 +5725,27 @@ USERCTL=no
|
||||
}
|
||||
},
|
||||
}
|
||||
- expected = {
|
||||
- "ifcfg-eno1": textwrap.dedent(
|
||||
- """\
|
||||
- AUTOCONNECT_PRIORITY=120
|
||||
- BOOTPROTO=dhcp
|
||||
- DEVICE=eno1
|
||||
- HWADDR=07-1c-c6-75-a4-be
|
||||
- METRIC=100
|
||||
- ONBOOT=yes
|
||||
- TYPE=Ethernet
|
||||
- USERCTL=no
|
||||
- """
|
||||
- ),
|
||||
- }
|
||||
for dhcp_ver in ("dhcp4", "dhcp6"):
|
||||
+ expected = {
|
||||
+ "ifcfg-eno1": textwrap.dedent(
|
||||
+ """\
|
||||
+ AUTOCONNECT_PRIORITY=120
|
||||
+ BOOTPROTO=dhcp
|
||||
+ DEVICE=eno1
|
||||
+ HWADDR=07-1c-c6-75-a4-be
|
||||
+ ONBOOT=yes
|
||||
+ TYPE=Ethernet
|
||||
+ USERCTL=no
|
||||
+ """
|
||||
+ ),
|
||||
+ }
|
||||
v2data = copy.deepcopy(v2base)
|
||||
if dhcp_ver == "dhcp6":
|
||||
- expected["ifcfg-eno1"] += "IPV6INIT=yes\nDHCPV6C=yes\n"
|
||||
+ expected[
|
||||
+ "ifcfg-eno1"
|
||||
+ ] += "IPV6INIT=yes\nDHCPV6C=yes\nIPV6_ROUTE_METRIC=100\n"
|
||||
+ else:
|
||||
+ expected["ifcfg-eno1"] += "IPV4_ROUTE_METRIC=100\n"
|
||||
v2data["ethernets"]["eno1"].update(
|
||||
{dhcp_ver: True, "{0}-overrides".format(dhcp_ver): overrides}
|
||||
)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -1,175 +0,0 @@
|
||||
From f1fdff22c356fcfb6ef546633e7872313dca36d1 Mon Sep 17 00:00:00 2001
|
||||
From: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
Date: Wed, 7 Aug 2024 16:47:52 +0200
|
||||
Subject: [PATCH 2/2] Get rid of gdisk dependency
|
||||
|
||||
RH-Author: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
RH-MergeRequest: 106: Get rid of gdisk dependency
|
||||
RH-Jira: RHEL-36093
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [2/2] 7814ff343d4f8e7db95ca1853ea0079fe577354a (vkuznets/cloud-init)
|
||||
|
||||
gdisk is not going to be shipped in RHEL10 as sfdisk is perfectly capable
|
||||
of dealing with GPT partition tables. cloud-init's upstream still relies on
|
||||
sgdisk for GPT and is reluctant to do the switch, do this downstream only
|
||||
for now.
|
||||
|
||||
X-downstream-only: true
|
||||
|
||||
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
---
|
||||
.distro/cloud-init.spec | 1 -
|
||||
cloudinit/config/cc_disk_setup.py | 98 +++++++++++++------------------
|
||||
2 files changed, 42 insertions(+), 57 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py
|
||||
index fa6a52d3..7638b425 100644
|
||||
--- a/cloudinit/config/cc_disk_setup.py
|
||||
+++ b/cloudinit/config/cc_disk_setup.py
|
||||
@@ -10,6 +10,7 @@
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
+import json
|
||||
from textwrap import dedent
|
||||
|
||||
from cloudinit import subp, util
|
||||
@@ -21,7 +22,6 @@ from cloudinit.settings import PER_INSTANCE
|
||||
|
||||
# Define the commands to use
|
||||
SFDISK_CMD = subp.which("sfdisk")
|
||||
-SGDISK_CMD = subp.which("sgdisk")
|
||||
LSBLK_CMD = subp.which("lsblk")
|
||||
BLKID_CMD = subp.which("blkid")
|
||||
BLKDEV_CMD = subp.which("blockdev")
|
||||
@@ -856,44 +856,32 @@ sgdisk_to_gpt_id = {
|
||||
gpt_id_to_sgdisk = {v: k for k, v in reversed(sgdisk_to_gpt_id.items())}
|
||||
|
||||
def check_partition_gpt_layout(device, layout):
|
||||
- prt_cmd = [SGDISK_CMD, "-p", device]
|
||||
+ # Use sfdisk's JSON output for reliability
|
||||
+ prt_cmd = [SFDISK_CMD, "-l", "-J", device]
|
||||
try:
|
||||
out, _err = subp.subp(prt_cmd, update_env=LANG_C_ENV)
|
||||
+ ptable = json.loads(out)["partitiontable"]
|
||||
+ if "partitions" in ptable:
|
||||
+ partitions = ptable["partitions"]
|
||||
+ else:
|
||||
+ partitions = []
|
||||
+
|
||||
except Exception as e:
|
||||
raise RuntimeError(
|
||||
"Error running partition command on %s\n%s" % (device, e)
|
||||
) from e
|
||||
|
||||
- out_lines = iter(out.splitlines())
|
||||
- # Skip header. Output looks like:
|
||||
- # ***************************************************************
|
||||
- # Found invalid GPT and valid MBR; converting MBR to GPT format
|
||||
- # in memory.
|
||||
- # ***************************************************************
|
||||
- #
|
||||
- # Disk /dev/vdb: 83886080 sectors, 40.0 GiB
|
||||
- # Logical sector size: 512 bytes
|
||||
- # Disk identifier (GUID): 8A7F11AD-3953-491B-8051-077E01C8E9A7
|
||||
- # Partition table holds up to 128 entries
|
||||
- # First usable sector is 34, last usable sector is 83886046
|
||||
- # Partitions will be aligned on 2048-sector boundaries
|
||||
- # Total free space is 83476413 sectors (39.8 GiB)
|
||||
- #
|
||||
- # Number Start (sector) End (sector) Size Code Name
|
||||
- # 1 2048 206847 100.0 MiB 0700 Microsoft basic data
|
||||
- for line in out_lines:
|
||||
- if line.strip().startswith("Number"):
|
||||
- break
|
||||
-
|
||||
- codes = [line.strip().split()[5] for line in out_lines]
|
||||
- cleaned = []
|
||||
-
|
||||
- # user would expect a code '83' to be Linux, but sgdisk outputs 8300.
|
||||
- for code in codes:
|
||||
- if len(code) == 4 and code.endswith("00"):
|
||||
- code = code[0:2]
|
||||
- cleaned.append(code)
|
||||
- return cleaned
|
||||
+ found_layout = []
|
||||
+ for part in partitions:
|
||||
+ if part["type"] in gpt_id_to_sgdisk:
|
||||
+ ptype = gpt_id_to_sgdisk[part["type"]]
|
||||
+ if len(ptype) == 4 and ptype[-2:] == "00":
|
||||
+ ptype = ptype[0:2]
|
||||
+ found_layout.append(ptype)
|
||||
+ else:
|
||||
+ # Unknown GPT UUID, using standard Linux
|
||||
+ found_layout.append("83")
|
||||
+ return found_layout
|
||||
|
||||
|
||||
def check_partition_layout(table_type, device, layout):
|
||||
@@ -1066,11 +1054,11 @@ def get_partition_layout(table_type, size, layout):
|
||||
This is a future proofing function. To add support for
|
||||
other layouts, simply add a "get_partition_%s_layout"
|
||||
function.
|
||||
+
|
||||
+ RHEL-only: sfdisk is used both for GPT and MBR
|
||||
"""
|
||||
- if "mbr" == table_type:
|
||||
+ if table_type in ["gpt", "mbr"]:
|
||||
return get_partition_mbr_layout(size, layout)
|
||||
- elif "gpt" == table_type:
|
||||
- return get_partition_gpt_layout(size, layout)
|
||||
raise RuntimeError("Unable to determine table type")
|
||||
|
||||
|
||||
@@ -1110,28 +1098,26 @@ def exec_mkpart_mbr(device, layout):
|
||||
|
||||
|
||||
def exec_mkpart_gpt(device, layout):
|
||||
+ prt_cmd = [SFDISK_CMD, "-X", "gpt", "--force", device]
|
||||
try:
|
||||
- subp.subp([SGDISK_CMD, "-Z", device])
|
||||
- for index, (partition_type, (start, end)) in enumerate(layout):
|
||||
- index += 1
|
||||
- subp.subp(
|
||||
- [
|
||||
- SGDISK_CMD,
|
||||
- "-n",
|
||||
- "{}:{}:{}".format(index, start, end),
|
||||
- device,
|
||||
- ]
|
||||
- )
|
||||
- if partition_type is not None:
|
||||
- # convert to a 4 char (or more) string right padded with 0
|
||||
- # 82 -> 8200. 'Linux' -> 'Linux'
|
||||
- pinput = str(partition_type).ljust(4, "0")
|
||||
- subp.subp(
|
||||
- [SGDISK_CMD, "-t", "{}:{}".format(index, pinput), device]
|
||||
- )
|
||||
- except Exception:
|
||||
- LOG.warning("Failed to partition device %s", device)
|
||||
- raise
|
||||
+ layout_fixed = []
|
||||
+ # convert partition UUIDs to GPT UUIDs
|
||||
+ for part in layout.split('\n'):
|
||||
+ (pstart, psize, ptype) = part.split(',')
|
||||
+ if len(ptype) == 2:
|
||||
+ ptype = ptype + "00"
|
||||
+ if ptype.upper() in sgdisk_to_gpt_id:
|
||||
+ ptype = sgdisk_to_gpt_id[ptype]
|
||||
+ else:
|
||||
+ # Use standard Linux for unknown ids
|
||||
+ ptype = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
|
||||
+ layout_fixed.append(','.join([pstart, psize, ptype]))
|
||||
+ layout = '\n'.join(layout_fixed)
|
||||
+ subp.subp(prt_cmd, data="%s\n" % layout)
|
||||
+ except Exception as e:
|
||||
+ raise RuntimeError(
|
||||
+ "Failed to partition device %s\n%s" % (device, e)
|
||||
+ ) from e
|
||||
|
||||
read_parttbl(device)
|
||||
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,44 @@
|
||||
From 7f3b0ff968409a880596e04aece4e4c504fb9c64 Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Mon, 29 Jan 2024 12:03:36 -0700
|
||||
Subject: [PATCH] ci: Pin pytest<8.0.0. (#4816)
|
||||
|
||||
The latest pytest release broke some tests in non-obvious ways. Pin
|
||||
the version for now so that CI passes.
|
||||
|
||||
(cherry picked from commit 7c96c9cd9318e816ce4564b58a2c98271363c447)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
integration-requirements.txt | 2 +-
|
||||
test-requirements.txt | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/integration-requirements.txt b/integration-requirements.txt
|
||||
index 1f8b54a5..c0792d63 100644
|
||||
--- a/integration-requirements.txt
|
||||
+++ b/integration-requirements.txt
|
||||
@@ -7,7 +7,7 @@ pycloudlib>=5.10.0,<1!6
|
||||
# test/unittests/conftest.py to be loaded by our integration-tests tox env
|
||||
# resulting in an unmet dependency issue:
|
||||
# https://github.com/pytest-dev/pytest/issues/11104
|
||||
-pytest!=7.3.2
|
||||
+pytest!=7.3.2,<8.0.0
|
||||
|
||||
packaging
|
||||
passlib
|
||||
diff --git a/test-requirements.txt b/test-requirements.txt
|
||||
index 46a98b4c..3d2480fd 100644
|
||||
--- a/test-requirements.txt
|
||||
+++ b/test-requirements.txt
|
||||
@@ -4,7 +4,7 @@
|
||||
# test/unittests/conftest.py to be loaded by our integration-tests tox env
|
||||
# resulting in an unmet dependency issue:
|
||||
# https://github.com/pytest-dev/pytest/issues/11104
|
||||
-pytest!=7.3.2
|
||||
+pytest!=7.3.2,<8.0.0
|
||||
|
||||
pytest-cov
|
||||
pytest-mock
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,248 @@
|
||||
From 038a391b7016f16a7336d67965762a7dbf3ba662 Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Tue, 5 Nov 2024 04:07:36 +0530
|
||||
Subject: [PATCH] Prevent NM from handling DNS when network interfaces have DNS
|
||||
config (#5846)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 150: Prevent NM from handling DNS when network interfaces have DNS config (#5846)
|
||||
RH-Jira: RHEL-65778
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] b464ef219eba1a0c718dda5ed96b6f88e1273f99
|
||||
|
||||
In the change under PR #5401, we use global DNS configuration as well as
|
||||
DNS and search domain information from interface config and use it to populate
|
||||
/etc/resolv.conf. Therefore, if either or both global DNS/search domain config
|
||||
is present along with per-interface DNS/search domain information, we should add
|
||||
a network manager configuration to prevent network manager from manipulating
|
||||
/etc/resolv.conf.
|
||||
This is in addition to what we already do when only global DNS data is
|
||||
configured.
|
||||
|
||||
Fixes bug added in 1b8030e0 .
|
||||
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
(cherry picked from commit 2df49b652471999434f06d9d83ed9db8b4055895)
|
||||
---
|
||||
cloudinit/net/sysconfig.py | 28 +++++--
|
||||
tests/unittests/test_net.py | 158 ++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 181 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
||||
index e4a65187f..4898a2fd1 100644
|
||||
--- a/cloudinit/net/sysconfig.py
|
||||
+++ b/cloudinit/net/sysconfig.py
|
||||
@@ -905,17 +905,35 @@ class Renderer(renderer.Renderer):
|
||||
|
||||
@staticmethod
|
||||
def _render_networkmanager_conf(network_state, templates=None):
|
||||
+ iface_dns = False
|
||||
content = networkmanager_conf.NetworkManagerConf("")
|
||||
-
|
||||
- # If DNS server information is provided, configure
|
||||
- # NetworkManager to not manage dns, so that /etc/resolv.conf
|
||||
- # does not get clobbered.
|
||||
+ # check if there is interface specific DNS information configured
|
||||
+ for iface in network_state.iter_interfaces():
|
||||
+ for subnet in iface["subnets"]:
|
||||
+ if "dns_nameservers" in subnet or "dns_search" in subnet:
|
||||
+ iface_dns = True
|
||||
+ break
|
||||
+ if (
|
||||
+ not iface_dns
|
||||
+ and "dns" in iface
|
||||
+ and (iface["dns"]["nameservers"] or iface["dns"]["search"])
|
||||
+ ):
|
||||
+ iface_dns = True
|
||||
+ break
|
||||
+
|
||||
+ # If DNS server and/or dns search information is provided either
|
||||
+ # globally or per interface basis, configure NetworkManager to
|
||||
+ # not manage dns, so that /etc/resolv.conf does not get clobbered.
|
||||
# This is not required for NetworkManager renderer as it
|
||||
# does not write /etc/resolv.conf directly. DNS information is
|
||||
# written to the interface keyfile and NetworkManager is then
|
||||
# responsible for using the DNS information from the keyfile,
|
||||
# including managing /etc/resolv.conf.
|
||||
- if network_state.dns_nameservers:
|
||||
+ if (
|
||||
+ network_state.dns_nameservers
|
||||
+ or network_state.dns_searchdomains
|
||||
+ or iface_dns
|
||||
+ ):
|
||||
content.set_section_keypair("main", "dns", "none")
|
||||
|
||||
if len(content) == 0:
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index ddb45dc69..47b36d052 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -967,6 +967,164 @@ dns = none
|
||||
),
|
||||
],
|
||||
},
|
||||
+ {
|
||||
+ "in_data": {
|
||||
+ "networks": [
|
||||
+ {
|
||||
+ "network_id": "dacd568d-5be6-4786-91fe-750c374b78b4",
|
||||
+ "type": "ipv4",
|
||||
+ "netmask": "255.255.252.0",
|
||||
+ "link": "eth0",
|
||||
+ "routes": [
|
||||
+ {
|
||||
+ "netmask": "0.0.0.0",
|
||||
+ "network": "0.0.0.0",
|
||||
+ "gateway": "172.19.3.254",
|
||||
+ }
|
||||
+ ],
|
||||
+ "ip_address": "172.19.1.34",
|
||||
+ "dns_search": ["example3.com"],
|
||||
+ "dns_nameservers": ["172.19.0.12"],
|
||||
+ "id": "network0",
|
||||
+ }
|
||||
+ ],
|
||||
+ "links": [
|
||||
+ {
|
||||
+ "ethernet_mac_address": "fa:16:3e:ed:9a:59",
|
||||
+ "mtu": None,
|
||||
+ "type": "physical",
|
||||
+ "id": "eth0",
|
||||
+ },
|
||||
+ ],
|
||||
+ },
|
||||
+ "in_macs": {
|
||||
+ "fa:16:3e:ed:9a:59": "eth0",
|
||||
+ },
|
||||
+ "out_sysconfig_opensuse": [
|
||||
+ (
|
||||
+ "etc/sysconfig/network/ifcfg-eth0",
|
||||
+ """
|
||||
+# Created by cloud-init automatically, do not edit.
|
||||
+#
|
||||
+BOOTPROTO=static
|
||||
+IPADDR=172.19.1.34
|
||||
+LLADDR=fa:16:3e:ed:9a:59
|
||||
+NETMASK=255.255.252.0
|
||||
+STARTMODE=auto
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ (
|
||||
+ "etc/resolv.conf",
|
||||
+ """
|
||||
+; Created by cloud-init automatically, do not edit.
|
||||
+;
|
||||
+nameserver 172.19.0.12
|
||||
+search example3.com
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ (
|
||||
+ "etc/NetworkManager/conf.d/99-cloud-init.conf",
|
||||
+ """
|
||||
+# Created by cloud-init automatically, do not edit.
|
||||
+#
|
||||
+[main]
|
||||
+dns = none
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ (
|
||||
+ "etc/udev/rules.d/85-persistent-net-cloud-init.rules",
|
||||
+ "".join(
|
||||
+ [
|
||||
+ 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
|
||||
+ 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n',
|
||||
+ ]
|
||||
+ ),
|
||||
+ ),
|
||||
+ ],
|
||||
+ "out_sysconfig_rhel": [
|
||||
+ (
|
||||
+ "etc/sysconfig/network-scripts/ifcfg-eth0",
|
||||
+ """
|
||||
+# Created by cloud-init automatically, do not edit.
|
||||
+#
|
||||
+AUTOCONNECT_PRIORITY=120
|
||||
+BOOTPROTO=none
|
||||
+DEFROUTE=yes
|
||||
+DEVICE=eth0
|
||||
+DNS1=172.19.0.12
|
||||
+DOMAIN=example3.com
|
||||
+GATEWAY=172.19.3.254
|
||||
+HWADDR=fa:16:3e:ed:9a:59
|
||||
+IPADDR=172.19.1.34
|
||||
+NETMASK=255.255.252.0
|
||||
+ONBOOT=yes
|
||||
+TYPE=Ethernet
|
||||
+USERCTL=no
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ (
|
||||
+ "etc/resolv.conf",
|
||||
+ """
|
||||
+; Created by cloud-init automatically, do not edit.
|
||||
+;
|
||||
+nameserver 172.19.0.12
|
||||
+search example3.com
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ (
|
||||
+ "etc/NetworkManager/conf.d/99-cloud-init.conf",
|
||||
+ """
|
||||
+# Created by cloud-init automatically, do not edit.
|
||||
+#
|
||||
+[main]
|
||||
+dns = none
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ (
|
||||
+ "etc/udev/rules.d/70-persistent-net.rules",
|
||||
+ "".join(
|
||||
+ [
|
||||
+ 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
|
||||
+ 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n',
|
||||
+ ]
|
||||
+ ),
|
||||
+ ),
|
||||
+ ],
|
||||
+ "expected_network_manager": [
|
||||
+ (
|
||||
+ "".join(
|
||||
+ [
|
||||
+ "etc/NetworkManager/system-connections",
|
||||
+ "/cloud-init-eth0.nmconnection",
|
||||
+ ]
|
||||
+ ),
|
||||
+ """
|
||||
+# Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+[connection]
|
||||
+id=cloud-init eth0
|
||||
+uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
|
||||
+autoconnect-priority=120
|
||||
+type=ethernet
|
||||
+
|
||||
+[user]
|
||||
+org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+[ethernet]
|
||||
+mac-address=FA:16:3E:ED:9A:59
|
||||
+
|
||||
+[ipv4]
|
||||
+method=manual
|
||||
+may-fail=false
|
||||
+address1=172.19.1.34/22
|
||||
+route1=0.0.0.0/0,172.19.3.254
|
||||
+dns=172.19.0.12;
|
||||
+dns-search=example3.com;
|
||||
+
|
||||
+""".lstrip(),
|
||||
+ ),
|
||||
+ ],
|
||||
+ },
|
||||
{
|
||||
"in_data": {
|
||||
"services": [{"type": "dns", "address": "172.19.0.12"}],
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,65 @@
|
||||
From cc31dc321ae35995ceff93e67aaf0b0c660aa890 Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Tue, 12 Mar 2024 12:52:10 +0530
|
||||
Subject: [PATCH] Retain exit code in cloud-init status for recoverable errors
|
||||
|
||||
RH-Author: Ani Sinha <None>
|
||||
RH-MergeRequest: 71: Retain exit code in cloud-init status for recoverable errors
|
||||
RH-Jira: RHEL-28549
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [1/1] 00934ade88c481c012bc1947fa44e5ed59f82858 (anisinha/cloud-init)
|
||||
|
||||
Version 23.4 of cloud-init changed the status code reported by cloud-init for
|
||||
recoverable errors from 0 to 2. Please see the commit
|
||||
70acb7f2a30d58 ("Add support for cloud-init "degraded" state (#4500)")
|
||||
|
||||
This change has the potential to break customers who are expecting a 0 status
|
||||
and where warnings can be expected. Hence, revert the status code from 2 to 0
|
||||
even in case of recoverable errors. This retains the old behavior and hence
|
||||
avoids breaking scripts and software stack that expects 0 on the end user side.
|
||||
|
||||
Cannonical has made a similar change downstream for similar reasons. Please see
|
||||
https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/2048522
|
||||
and the corresponding downstream patch:
|
||||
https://github.com/canonical/cloud-init/pull/4747/commits/adce34bfd214e4eecdf87329486f30f0898dd303
|
||||
|
||||
This patch has limited risk as it narrowly only restores the old status
|
||||
code for recoverable errors and does not modify anything else.
|
||||
|
||||
X-downstream-only: true
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/cmd/status.py | 2 +-
|
||||
tests/unittests/cmd/test_status.py | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/cmd/status.py b/cloudinit/cmd/status.py
|
||||
index f5ee9c11..849c80bc 100644
|
||||
--- a/cloudinit/cmd/status.py
|
||||
+++ b/cloudinit/cmd/status.py
|
||||
@@ -225,7 +225,7 @@ def handle_status_args(name, args) -> int:
|
||||
return 1
|
||||
# Recoverable error
|
||||
elif details.status in UXAppStatusDegradedMap.values():
|
||||
- return 2
|
||||
+ return 0
|
||||
return 0
|
||||
|
||||
|
||||
diff --git a/tests/unittests/cmd/test_status.py b/tests/unittests/cmd/test_status.py
|
||||
index 6c85a59a..567b517a 100644
|
||||
--- a/tests/unittests/cmd/test_status.py
|
||||
+++ b/tests/unittests/cmd/test_status.py
|
||||
@@ -636,7 +636,7 @@ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
|
||||
},
|
||||
None,
|
||||
MyArgs(long=False, wait=False, format="json"),
|
||||
- 2,
|
||||
+ 0,
|
||||
{
|
||||
"boot_status_code": "enabled-by-kernel-cmdline",
|
||||
"datasource": "nocloud",
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,242 @@
|
||||
From 72b2deeafd9276d15f20831f01b2f8c44616f33d Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Tue, 23 Jan 2024 11:47:35 -0700
|
||||
Subject: [PATCH] Revert "Use grep for faster parsing of cloud config in
|
||||
ds-identify (#4327)"
|
||||
|
||||
RH-Author: Ani Sinha <None>
|
||||
RH-MergeRequest: 67: Revert "Use grep for faster parsing of cloud config in ds-identify (#4327)"
|
||||
RH-Jira: RHEL-22255
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] 5997598254cd16ea7f26d87212b0f09920fcdf50 (anisinha/cloud-init)
|
||||
|
||||
This reverts commit 816e05d4830f5e789f1f85ef926e2849156bff3a.
|
||||
|
||||
Reopens LP: 2030729
|
||||
Fixes GH-4794
|
||||
|
||||
(cherry picked from commit 8ff94fe9493ad88344eb8bbf2f023c6ba2db5206)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
tests/unittests/test_ds_identify.py | 146 +---------------------------
|
||||
tools/ds-identify | 31 +++---
|
||||
2 files changed, 15 insertions(+), 162 deletions(-)
|
||||
|
||||
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
|
||||
index ca206fb5..ba0bf779 100644
|
||||
--- a/tests/unittests/test_ds_identify.py
|
||||
+++ b/tests/unittests/test_ds_identify.py
|
||||
@@ -57,146 +57,6 @@ BLKID_UEFI_UBUNTU = [
|
||||
]
|
||||
|
||||
|
||||
-DEFAULT_CLOUD_CONFIG = """\
|
||||
-# The top level settings are used as module
|
||||
-# and base configuration.
|
||||
-# A set of users which may be applied and/or used by various modules
|
||||
-# when a 'default' entry is found it will reference the 'default_user'
|
||||
-# from the distro configuration specified below
|
||||
-users:
|
||||
- - default
|
||||
-
|
||||
-# If this is set, 'root' will not be able to ssh in and they
|
||||
-# will get a message to login instead as the default $user
|
||||
-disable_root: true
|
||||
-
|
||||
-# This will cause the set+update hostname module to not operate (if true)
|
||||
-preserve_hostname: false
|
||||
-
|
||||
-# If you use datasource_list array, keep array items in a single line.
|
||||
-# If you use multi line array, ds-identify script won't read array items.
|
||||
-# Example datasource config
|
||||
-# datasource:
|
||||
-# Ec2:
|
||||
-# metadata_urls: [ 'blah.com' ]
|
||||
-# timeout: 5 # (defaults to 50 seconds)
|
||||
-# max_wait: 10 # (defaults to 120 seconds)
|
||||
-
|
||||
-# The modules that run in the 'init' stage
|
||||
-cloud_init_modules:
|
||||
- - migrator
|
||||
- - seed_random
|
||||
- - bootcmd
|
||||
- - write-files
|
||||
- - growpart
|
||||
- - resizefs
|
||||
- - disk_setup
|
||||
- - mounts
|
||||
- - set_hostname
|
||||
- - update_hostname
|
||||
- - update_etc_hosts
|
||||
- - ca-certs
|
||||
- - rsyslog
|
||||
- - users-groups
|
||||
- - ssh
|
||||
-
|
||||
-# The modules that run in the 'config' stage
|
||||
-cloud_config_modules:
|
||||
- - wireguard
|
||||
- - snap
|
||||
- - ubuntu_autoinstall
|
||||
- - ssh-import-id
|
||||
- - keyboard
|
||||
- - locale
|
||||
- - set-passwords
|
||||
- - grub-dpkg
|
||||
- - apt-pipelining
|
||||
- - apt-configure
|
||||
- - ubuntu-advantage
|
||||
- - ntp
|
||||
- - timezone
|
||||
- - disable-ec2-metadata
|
||||
- - runcmd
|
||||
- - byobu
|
||||
-
|
||||
-# The modules that run in the 'final' stage
|
||||
-cloud_final_modules:
|
||||
- - package-update-upgrade-install
|
||||
- - fan
|
||||
- - landscape
|
||||
- - lxd
|
||||
- - ubuntu-drivers
|
||||
- - write-files-deferred
|
||||
- - puppet
|
||||
- - chef
|
||||
- - ansible
|
||||
- - mcollective
|
||||
- - salt-minion
|
||||
- - reset_rmc
|
||||
- - refresh_rmc_and_interface
|
||||
- - rightscale_userdata
|
||||
- - scripts-vendor
|
||||
- - scripts-per-once
|
||||
- - scripts-per-boot
|
||||
- - scripts-per-instance
|
||||
- - scripts-user
|
||||
- - ssh-authkey-fingerprints
|
||||
- - keys-to-console
|
||||
- - install-hotplug
|
||||
- - phone-home
|
||||
- - final-message
|
||||
- - power-state-change
|
||||
-
|
||||
-# System and/or distro specific settings
|
||||
-# (not accessible to handlers/transforms)
|
||||
-system_info:
|
||||
- # This will affect which distro class gets used
|
||||
- distro: ubuntu
|
||||
- # Default user name + that default users groups (if added/used)
|
||||
- default_user:
|
||||
- name: ubuntu
|
||||
- lock_passwd: True
|
||||
- gecos: Ubuntu
|
||||
- groups: [adm, audio, cdrom, floppy, lxd, netdev, plugdev, sudo, video]
|
||||
- sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
- shell: /bin/bash
|
||||
- network:
|
||||
- renderers: ['netplan', 'eni', 'sysconfig']
|
||||
- activators: ['netplan', 'eni', 'network-manager', 'networkd']
|
||||
- # Automatically discover the best ntp_client
|
||||
- ntp_client: auto
|
||||
- # Other config here will be given to the distro class and/or path classes
|
||||
- paths:
|
||||
- cloud_dir: /var/lib/cloud/
|
||||
- templates_dir: /etc/cloud/templates/
|
||||
- package_mirrors:
|
||||
- - arches: [i386, amd64]
|
||||
- failsafe:
|
||||
- primary: http://archive.ubuntu.com/ubuntu
|
||||
- security: http://security.ubuntu.com/ubuntu
|
||||
- search:
|
||||
- primary:
|
||||
- - http://%(ec2_region)s.ec2.archive.ubuntu.com/ubuntu/
|
||||
- - http://%(availability_zone)s.clouds.archive.ubuntu.com/ubuntu/
|
||||
- - http://%(region)s.clouds.archive.ubuntu.com/ubuntu/
|
||||
- security: []
|
||||
- - arches: [arm64, armel, armhf]
|
||||
- failsafe:
|
||||
- primary: http://ports.ubuntu.com/ubuntu-ports
|
||||
- security: http://ports.ubuntu.com/ubuntu-ports
|
||||
- search:
|
||||
- primary:
|
||||
- - http://%(ec2_region)s.ec2.ports.ubuntu.com/ubuntu-ports/
|
||||
- - http://%(availability_zone)s.clouds.ports.ubuntu.com/ubuntu-ports/
|
||||
- - http://%(region)s.clouds.ports.ubuntu.com/ubuntu-ports/
|
||||
- security: []
|
||||
- - arches: [default]
|
||||
- failsafe:
|
||||
- primary: http://ports.ubuntu.com/ubuntu-ports
|
||||
- security: http://ports.ubuntu.com/ubuntu-ports
|
||||
- ssh_svcname: ssh
|
||||
-"""
|
||||
-
|
||||
POLICY_FOUND_ONLY = "search,found=all,maybe=none,notfound=disabled"
|
||||
POLICY_FOUND_OR_MAYBE = "search,found=all,maybe=all,notfound=disabled"
|
||||
DI_DEFAULT_POLICY = "search,found=all,maybe=all,notfound=disabled"
|
||||
@@ -279,10 +139,6 @@ class DsIdentifyBase(CiTestCase):
|
||||
if files is None:
|
||||
files = {}
|
||||
|
||||
- cloudcfg = "etc/cloud/cloud.cfg"
|
||||
- if cloudcfg not in files:
|
||||
- files[cloudcfg] = DEFAULT_CLOUD_CONFIG
|
||||
-
|
||||
if rootd is None:
|
||||
rootd = self.tmp_dir()
|
||||
|
||||
@@ -1305,7 +1161,7 @@ VALID_CFG = {
|
||||
# Also include a datasource list of more than just
|
||||
# [NoCloud, None], because that would automatically select
|
||||
# NoCloud without checking
|
||||
- "etc/cloud/cloud.cfg": dedent(
|
||||
+ "/etc/cloud/cloud.cfg": dedent(
|
||||
"""\
|
||||
datasource_list: [ Azure, Openstack, NoCloud, None ]
|
||||
datasource:
|
||||
diff --git a/tools/ds-identify b/tools/ds-identify
|
||||
index 7a537278..ec2cc18a 100755
|
||||
--- a/tools/ds-identify
|
||||
+++ b/tools/ds-identify
|
||||
@@ -777,24 +777,21 @@ check_config() {
|
||||
if [ "$1" = "$files" -a ! -f "$1" ]; then
|
||||
return 1
|
||||
fi
|
||||
- local line="" ret="" found=0 found_fn="" oifs="$IFS" out=""
|
||||
- out=$(grep "$key\"\?:" "$@" 2>/dev/null)
|
||||
- IFS=${CR}
|
||||
- for line in $out; do
|
||||
- # drop '# comment'
|
||||
- line=${line%%#*}
|
||||
- # if more than one file was 'grep'ed, then grep will output filename:
|
||||
- # but if only one file, line will not be prefixed.
|
||||
- if [ $# -eq 1 ]; then
|
||||
- found_fn="$1"
|
||||
- else
|
||||
- found_fn="${line%%:*}"
|
||||
- line=${line#*:}
|
||||
- fi
|
||||
- ret=${line#*: };
|
||||
- found=$((found+1))
|
||||
+ local fname="" line="" ret="" found=0 found_fn=""
|
||||
+ # shellcheck disable=2094
|
||||
+ for fname in "$@"; do
|
||||
+ [ -f "$fname" ] || continue
|
||||
+ while read line; do
|
||||
+ line=${line%%#*}
|
||||
+ case "$line" in
|
||||
+ $key:\ *|"${key}":)
|
||||
+ ret=${line#*:};
|
||||
+ ret=${ret# };
|
||||
+ found=$((found+1))
|
||||
+ found_fn="$fname";;
|
||||
+ esac
|
||||
+ done <"$fname"
|
||||
done
|
||||
- IFS="$oifs"
|
||||
if [ $found -ne 0 ]; then
|
||||
_RET="$ret"
|
||||
_RET_fname="$found_fn"
|
||||
--
|
||||
2.39.3
|
||||
|
@ -1,409 +0,0 @@
|
||||
From f370030e178c928592e6c359cc528b7033956d79 Mon Sep 17 00:00:00 2001
|
||||
From: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
Date: Wed, 7 Aug 2024 16:43:55 +0200
|
||||
Subject: [PATCH 1/2] cc_disk_setup: add sgdisk to sfdisk convertion
|
||||
dictionaries
|
||||
|
||||
RH-Author: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
RH-MergeRequest: 106: Get rid of gdisk dependency
|
||||
RH-Jira: RHEL-36093
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [1/2] c6a6eca2b1496bccfe404e902417f02f828682aa (vkuznets/cloud-init)
|
||||
|
||||
In preparation to using sfdisk instead of sgdisk, make it possible to
|
||||
convert between sgdisk-invented ids and standard GPT UUIDs.
|
||||
|
||||
No functional change.
|
||||
|
||||
X-downstream-only: true
|
||||
|
||||
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
---
|
||||
cloudinit/config/cc_disk_setup.py | 370 ++++++++++++++++++++++++++++++
|
||||
1 file changed, 370 insertions(+)
|
||||
|
||||
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py
|
||||
index 0ccf6a35..fa6a52d3 100644
|
||||
--- a/cloudinit/config/cc_disk_setup.py
|
||||
+++ b/cloudinit/config/cc_disk_setup.py
|
||||
@@ -484,6 +484,376 @@ def check_partition_mbr_layout(device, layout):
|
||||
found_layout.append(type_label)
|
||||
return found_layout
|
||||
|
||||
+SFDISK_CMD="sfdisk"
|
||||
+
|
||||
+# gdisk uses its own invented ids, convert them to standard GPT ones. From gdisk sources:
|
||||
+# grep " AddType" parttypes.cc | sed -e 's,AddType(,,' -e 's,"\,.*$,"\,,' -e 's,0x,",' -e 's,\,,":,' | tr "[a-z]" "[A-Z]"
|
||||
+sgdisk_to_gpt_id = {
|
||||
+ "0000": "00000000-0000-0000-0000-000000000000",
|
||||
+ "0100": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "0400": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "0600": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "0700": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "0701": "558D43C5-A1AC-43C0-AAC8-D1472B2923D1",
|
||||
+ "0702": "90B6FF38-B98F-4358-A21F-48F35B4A8AD3",
|
||||
+ "0B00": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "0C00": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "0C01": "E3C9E316-0B5C-4DB8-817D-F92DF00215AE",
|
||||
+ "0E00": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1100": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1400": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1600": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1700": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1B00": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1C00": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "1E00": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7",
|
||||
+ "2700": "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC",
|
||||
+ "3000": "7412F7D5-A156-4B13-81DC-867174929325",
|
||||
+ "3001": "D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149",
|
||||
+ "3900": "C91818F9-8025-47AF-89D2-F030D7000C2C",
|
||||
+ "4100": "9E1A2D38-C612-4316-AA26-8B49521E5A8B",
|
||||
+ "4200": "AF9B60A0-1431-4F62-BC68-3311714A69AD",
|
||||
+ "4201": "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3",
|
||||
+ "4202": "E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D",
|
||||
+ "7501": "37AFFC90-EF7D-4E96-91C3-2D7AE055B174",
|
||||
+ "7F00": "FE3A2A5D-4F32-41A7-B725-ACCC3285A309",
|
||||
+ "7F01": "3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC",
|
||||
+ "7F02": "2E0A753D-9E48-43B0-8337-B15192CB1B5E",
|
||||
+ "7F03": "CAB6E88E-ABF3-4102-A07A-D4BB9BE3C1D3",
|
||||
+ "7F04": "09845860-705F-4BB5-B16C-8A8A099CAF52",
|
||||
+ "7F05": "3F0F8318-F146-4E6B-8222-C28C8F02E0D5",
|
||||
+ "8200": "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F",
|
||||
+ "8300": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
|
||||
+ "8301": "8DA63339-0007-60C0-C436-083AC8230908",
|
||||
+ "8302": "933AC7E1-2EB4-4F13-B844-0E14E2AEF915",
|
||||
+ "8303": "44479540-F297-41B2-9AF7-D131D5F0458A",
|
||||
+ "8304": "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709",
|
||||
+ "8305": "B921B045-1DF0-41C3-AF44-4C6F280D3FAE",
|
||||
+ "8306": "3B8F8425-20E0-4F3B-907F-1A25A76F98E8",
|
||||
+ "8307": "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3",
|
||||
+ "8308": "7FFEC5C9-2D00-49B7-8941-3EA10A5586B7",
|
||||
+ "8309": "CA7D7CCB-63ED-4C53-861C-1742536059CC",
|
||||
+ "830A": "993D8D3D-F80E-4225-855A-9DAF8ED7EA97",
|
||||
+ "830B": "D13C5D3B-B5D1-422A-B29F-9454FDC89D76",
|
||||
+ "830C": "2C7357ED-EBD2-46D9-AEC1-23D437EC2BF5",
|
||||
+ "830D": "7386CDF2-203C-47A9-A498-F2ECCE45A2D6",
|
||||
+ "830E": "DF3300CE-D69F-4C92-978C-9BFB0F38D820",
|
||||
+ "830F": "86ED10D5-B607-45BB-8957-D350F23D0571",
|
||||
+ "8310": "4D21B016-B534-45C2-A9FB-5C16E091FD2D",
|
||||
+ "8311": "7EC6F557-3BC5-4ACA-B293-16EF5DF639D1",
|
||||
+ "8312": "773F91EF-66D4-49B5-BD83-D683BF40AD16",
|
||||
+ "8313": "75250D76-8CC6-458E-BD66-BD47CC81A812",
|
||||
+ "8314": "8484680C-9521-48C6-9C11-B0720656F69E",
|
||||
+ "8315": "7D0359A3-02B3-4F0A-865C-654403E70625",
|
||||
+ "8316": "B0E01050-EE5F-4390-949A-9101B17104E9",
|
||||
+ "8317": "4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA",
|
||||
+ "8318": "8F461B0D-14EE-4E81-9AA9-049B6FB97ABD",
|
||||
+ "8319": "77FF5F63-E7B6-4633-ACF4-1565B864C0E6",
|
||||
+ "831A": "C215D751-7BCD-4649-BE90-6627490A4C05",
|
||||
+ "831B": "6E11A4E7-FBCA-4DED-B9E9-E1A512BB664E",
|
||||
+ "831C": "6A491E03-3BE7-4545-8E38-83320E0EA880",
|
||||
+ "831D": "6523F8AE-3EB1-4E2A-A05A-18B695AE656F",
|
||||
+ "831E": "D27F46ED-2919-4CB8-BD25-9531F3C16534",
|
||||
+ "831F": "77055800-792C-4F94-B39A-98C91B762BB6",
|
||||
+ "8320": "E9434544-6E2C-47CC-BAE2-12D6DEAFB44C",
|
||||
+ "8321": "D113AF76-80EF-41B4-BDB6-0CFF4D3D4A25",
|
||||
+ "8322": "37C58C8A-D913-4156-A25F-48B1B64E07F0",
|
||||
+ "8323": "700BDA43-7A34-4507-B179-EEB93D7A7CA3",
|
||||
+ "8324": "1AACDB3B-5444-4138-BD9E-E5C2239B2346",
|
||||
+ "8325": "1DE3F1EF-FA98-47B5-8DCD-4A860A654D78",
|
||||
+ "8326": "912ADE1D-A839-4913-8964-A10EEE08FBD2",
|
||||
+ "8327": "C31C45E6-3F39-412E-80FB-4809C4980599",
|
||||
+ "8328": "60D5A7FE-8E7D-435C-B714-3DD8162144E1",
|
||||
+ "8329": "72EC70A6-CF74-40E6-BD49-4BDA08E8F224",
|
||||
+ "832A": "08A7ACEA-624C-4A20-91E8-6E0FA67D23F9",
|
||||
+ "832B": "5EEAD9A9-FE09-4A1E-A1D7-520D00531306",
|
||||
+ "832C": "C50CDD70-3862-4CC3-90E1-809A8C93EE2C",
|
||||
+ "832D": "E18CF08C-33EC-4C0D-8246-C6C6FB3DA024",
|
||||
+ "832E": "7978A683-6316-4922-BBEE-38BFF5A2FECC",
|
||||
+ "832F": "E611C702-575C-4CBE-9A46-434FA0BF7E3F",
|
||||
+ "8330": "773B2ABC-2A99-4398-8BF5-03BAAC40D02B",
|
||||
+ "8331": "57E13958-7331-4365-8E6E-35EEEE17C61B",
|
||||
+ "8332": "0F4868E9-9952-4706-979F-3ED3A473E947",
|
||||
+ "8333": "C97C1F32-BA06-40B4-9F22-236061B08AA8",
|
||||
+ "8334": "DC4A4480-6917-4262-A4EC-DB9384949F25",
|
||||
+ "8335": "7D14FEC5-CC71-415D-9D6C-06BF0B3C3EAF",
|
||||
+ "8336": "2C9739E2-F068-46B3-9FD0-01C5A9AFBCCA",
|
||||
+ "8337": "15BB03AF-77E7-4D4A-B12B-C0D084F7491C",
|
||||
+ "8338": "B933FB22-5C3F-4F91-AF90-E2BB0FA50702",
|
||||
+ "8339": "BEAEC34B-8442-439B-A40B-984381ED097D",
|
||||
+ "833A": "CD0F869B-D0FB-4CA0-B141-9EA87CC78D66",
|
||||
+ "833B": "8A4F5770-50AA-4ED3-874A-99B710DB6FEA",
|
||||
+ "833C": "55497029-C7C1-44CC-AA39-815ED1558630",
|
||||
+ "833D": "FC56D9E9-E6E5-4C06-BE32-E74407CE09A5",
|
||||
+ "833E": "24B2D975-0F97-4521-AFA1-CD531E421B8D",
|
||||
+ "833F": "F3393B22-E9AF-4613-A948-9D3BFBD0C535",
|
||||
+ "8340": "7A430799-F711-4C7E-8E5B-1D685BD48607",
|
||||
+ "8341": "579536F8-6A33-4055-A95A-DF2D5E2C42A8",
|
||||
+ "8342": "D7D150D2-2A04-4A33-8F12-16651205FF7B",
|
||||
+ "8343": "16B417F8-3E06-4F57-8DD2-9B5232F41AA6",
|
||||
+ "8344": "D212A430-FBC5-49F9-A983-A7FEEF2B8D0E",
|
||||
+ "8345": "906BD944-4589-4AAE-A4E4-DD983917446A",
|
||||
+ "8346": "9225A9A3-3C19-4D89-B4F6-EEFF88F17631",
|
||||
+ "8347": "98CFE649-1588-46DC-B2F0-ADD147424925",
|
||||
+ "8348": "AE0253BE-1167-4007-AC68-43926C14C5DE",
|
||||
+ "8349": "B6ED5582-440B-4209-B8DA-5FF7C419EA3D",
|
||||
+ "834A": "7AC63B47-B25C-463B-8DF8-B4A94E6C90E1",
|
||||
+ "834B": "B325BFBE-C7BE-4AB8-8357-139E652D2F6B",
|
||||
+ "834C": "966061EC-28E4-4B2E-B4A5-1F0A825A1D84",
|
||||
+ "834D": "8CCE0D25-C0D0-4A44-BD87-46331BF1DF67",
|
||||
+ "834E": "FCA0598C-D880-4591-8C16-4EDA05C7347C",
|
||||
+ "834F": "F46B2C26-59AE-48F0-9106-C50ED47F673D",
|
||||
+ "8350": "6E5A1BC8-D223-49B7-BCA8-37A5FCCEB996",
|
||||
+ "8351": "81CF9D90-7458-4DF4-8DCF-C8A3A404F09B",
|
||||
+ "8352": "46B98D8D-B55C-4E8F-AAB3-37FCA7F80752",
|
||||
+ "8353": "3C3D61FE-B5F3-414D-BB71-8739A694A4EF",
|
||||
+ "8354": "5843D618-EC37-48D7-9F12-CEA8E08768B2",
|
||||
+ "8355": "EE2B9983-21E8-4153-86D9-B6901A54D1CE",
|
||||
+ "8356": "BDB528A5-A259-475F-A87D-DA53FA736A07",
|
||||
+ "8357": "DF765D00-270E-49E5-BC75-F47BB2118B09",
|
||||
+ "8358": "CB1EE4E3-8CD0-4136-A0A4-AA61A32E8730",
|
||||
+ "8359": "8F1056BE-9B05-47C4-81D6-BE53128E5B54",
|
||||
+ "835A": "B663C618-E7BC-4D6D-90AA-11B756BB1797",
|
||||
+ "835B": "31741CC4-1A2A-4111-A581-E00B447D2D06",
|
||||
+ "835C": "2FB4BF56-07FA-42DA-8132-6B139F2026AE",
|
||||
+ "835D": "D46495B7-A053-414F-80F7-700C99921EF8",
|
||||
+ "835E": "143A70BA-CBD3-4F06-919F-6C05683A78BC",
|
||||
+ "835F": "42B0455F-EB11-491D-98D3-56145BA9D037",
|
||||
+ "8360": "6DB69DE6-29F4-4758-A7A5-962190F00CE3",
|
||||
+ "8361": "E98B36EE-32BA-4882-9B12-0CE14655F46A",
|
||||
+ "8362": "5AFB67EB-ECC8-4F85-AE8E-AC1E7C50E7D0",
|
||||
+ "8363": "BBA210A2-9C5D-45EE-9E87-FF2CCBD002D0",
|
||||
+ "8364": "43CE94D4-0F3D-4999-8250-B9DEAFD98E6E",
|
||||
+ "8365": "C919CC1F-4456-4EFF-918C-F75E94525CA5",
|
||||
+ "8366": "904E58EF-5C65-4A31-9C57-6AF5FC7C5DE7",
|
||||
+ "8367": "15DE6170-65D3-431C-916E-B0DCD8393F25",
|
||||
+ "8368": "D4A236E7-E873-4C07-BF1D-BF6CF7F1C3C6",
|
||||
+ "8369": "F5E2C20C-45B2-4FFA-BCE9-2A60737E1AAF",
|
||||
+ "836A": "1B31B5AA-ADD9-463A-B2ED-BD467FC857E7",
|
||||
+ "836B": "3A112A75-8729-4380-B4CF-764D79934448",
|
||||
+ "836C": "EFE0F087-EA8D-4469-821A-4C2A96A8386A",
|
||||
+ "836D": "3482388E-4254-435A-A241-766A065F9960",
|
||||
+ "836E": "C80187A5-73A3-491A-901A-017C3FA953E9",
|
||||
+ "836F": "B3671439-97B0-4A53-90F7-2D5A8F3AD47B",
|
||||
+ "8370": "41092B05-9FC8-4523-994F-2DEF0408B176",
|
||||
+ "8371": "5996FC05-109C-48DE-808B-23FA0830B676",
|
||||
+ "8372": "5C6E1C76-076A-457A-A0FE-F3B4CD21CE6E",
|
||||
+ "8373": "94F9A9A1-9971-427A-A400-50CB297F0F35",
|
||||
+ "8374": "D7FF812F-37D1-4902-A810-D76BA57B975A",
|
||||
+ "8375": "C23CE4FF-44BD-4B00-B2D4-B41B3419E02A",
|
||||
+ "8376": "8DE58BC2-2A43-460D-B14E-A76E4A17B47F",
|
||||
+ "8377": "B024F315-D330-444C-8461-44BBDE524E99",
|
||||
+ "8378": "97AE158D-F216-497B-8057-F7F905770F54",
|
||||
+ "8379": "05816CE2-DD40-4AC6-A61D-37D32DC1BA7D",
|
||||
+ "837A": "3E23CA0B-A4BC-4B4E-8087-5AB6A26AA8A9",
|
||||
+ "837B": "F2C2C7EE-ADCC-4351-B5C6-EE9816B66E16",
|
||||
+ "837C": "450DD7D1-3224-45EC-9CF2-A43A346D71EE",
|
||||
+ "837D": "C8BFBD1E-268E-4521-8BBA-BF314C399557",
|
||||
+ "837E": "0B888863-D7F8-4D9E-9766-239FCE4D58AF",
|
||||
+ "837F": "7007891D-D371-4A80-86A4-5CB875B9302E",
|
||||
+ "8380": "C3836A13-3137-45BA-B583-B16C50FE5EB4",
|
||||
+ "8381": "D2F9000A-7A18-453F-B5CD-4D32F77A7B32",
|
||||
+ "8382": "17440E4F-A8D0-467F-A46E-3912AE6EF2C5",
|
||||
+ "8383": "3F324816-667B-46AE-86EE-9B0C0C6C11B4",
|
||||
+ "8384": "4EDE75E2-6CCC-4CC8-B9C7-70334B087510",
|
||||
+ "8385": "E7BB33FB-06CF-4E81-8273-E543B413E2E2",
|
||||
+ "8386": "974A71C0-DE41-43C3-BE5D-5C5CCD1AD2C0",
|
||||
+ "8400": "D3BFE2DE-3DAF-11DF-BA40-E3A556D89593",
|
||||
+ "8401": "7C5222BD-8F5D-4087-9C00-BF9843C7B58C",
|
||||
+ "8500": "5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6",
|
||||
+ "8501": "3884DD41-8582-4404-B9A8-E9B84F2DF50E",
|
||||
+ "8502": "C95DC21A-DF0E-4340-8D7B-26CBFA9A03E0",
|
||||
+ "8503": "BE9067B9-EA49-4F15-B4F6-F36F8C9E1818",
|
||||
+ "8E00": "E6D6D379-F507-44C2-A23C-238F2A3DF928",
|
||||
+ "A000": "2568845D-2332-4675-BC39-8FA5A4748D15",
|
||||
+ "A001": "114EAFFE-1552-4022-B26E-9B053604CF84",
|
||||
+ "A002": "49A4D17F-93A3-45C1-A0DE-F50B2EBE2599",
|
||||
+ "A003": "4177C722-9E92-4AAB-8644-43502BFD5506",
|
||||
+ "A004": "EF32A33B-A409-486C-9141-9FFB711F6266",
|
||||
+ "A005": "20AC26BE-20B7-11E3-84C5-6CFDB94711E9",
|
||||
+ "A006": "38F428E6-D326-425D-9140-6E0EA133647C",
|
||||
+ "A007": "A893EF21-E428-470A-9E55-0668FD91A2D9",
|
||||
+ "A008": "DC76DDA9-5AC1-491C-AF42-A82591580C0D",
|
||||
+ "A009": "EBC597D0-2053-4B15-8B64-E0AAC75F4DB1",
|
||||
+ "A00A": "8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80",
|
||||
+ "A00B": "767941D0-2085-11E3-AD3B-6CFDB94711E9",
|
||||
+ "A00C": "AC6D7924-EB71-4DF8-B48D-E267B27148FF",
|
||||
+ "A00D": "C5A0AEEC-13EA-11E5-A1B1-001E67CA0C3C",
|
||||
+ "A00E": "BD59408B-4514-490D-BF12-9878D963F378",
|
||||
+ "A00F": "9FDAA6EF-4B3F-40D2-BA8D-BFF16BFB887B",
|
||||
+ "A010": "19A710A2-B3CA-11E4-B026-10604B889DCF",
|
||||
+ "A011": "193D1EA4-B3CA-11E4-B075-10604B889DCF",
|
||||
+ "A012": "DEA0BA2C-CBDD-4805-B4F9-F428251C3E98",
|
||||
+ "A013": "8C6B52AD-8A9E-4398-AD09-AE916E53AE2D",
|
||||
+ "A014": "05E044DF-92F1-4325-B69E-374A82E97D6E",
|
||||
+ "A015": "400FFDCD-22E0-47E7-9A23-F16ED9382388",
|
||||
+ "A016": "A053AA7F-40B8-4B1C-BA08-2F68AC71A4F4",
|
||||
+ "A017": "E1A6A689-0C8D-4CC6-B4E8-55A4320FBD8A",
|
||||
+ "A018": "098DF793-D712-413D-9D4E-89D711772228",
|
||||
+ "A019": "D4E0D938-B7FA-48C1-9D21-BC5ED5C4B203",
|
||||
+ "A01A": "20A0C19C-286A-42FA-9CE7-F64C3226A794",
|
||||
+ "A01B": "A19F205F-CCD8-4B6D-8F1E-2D9BC24CFFB1",
|
||||
+ "A01C": "66C9B323-F7FC-48B6-BF96-6F32E335A428",
|
||||
+ "A01D": "303E6AC3-AF15-4C54-9E9B-D9A8FBECF401",
|
||||
+ "A01E": "C00EEF24-7709-43D6-9799-DD2B411E7A3C",
|
||||
+ "A01F": "82ACC91F-357C-4A68-9C8F-689E1B1A23A1",
|
||||
+ "A020": "E2802D54-0545-E8A1-A1E8-C7A3E245ACD4",
|
||||
+ "A021": "65ADDCF4-0C5C-4D9A-AC2D-D90B5CBFCD03",
|
||||
+ "A022": "E6E98DA2-E22A-4D12-AB33-169E7DEAA507",
|
||||
+ "A023": "ED9E8101-05FA-46B7-82AA-8D58770D200B",
|
||||
+ "A024": "11406F35-1173-4869-807B-27DF71802812",
|
||||
+ "A025": "9D72D4E4-9958-42DA-AC26-BEA7A90B0434",
|
||||
+ "A026": "6C95E238-E343-4BA8-B489-8681ED22AD0B",
|
||||
+ "A027": "EBBEADAF-22C9-E33B-8F5D-0E81686A68CB",
|
||||
+ "A028": "0A288B1F-22C9-E33B-8F5D-0E81686A68CB",
|
||||
+ "A029": "57B90A16-22C9-E33B-8F5D-0E81686A68CB",
|
||||
+ "A02A": "638FF8E2-22C9-E33B-8F5D-0E81686A68CB",
|
||||
+ "A02B": "2013373E-1AC4-4131-BFD8-B6A7AC638772",
|
||||
+ "A02C": "2C86E742-745E-4FDD-BFD8-B6A7AC638772",
|
||||
+ "A02D": "DE7D4029-0F5B-41C8-AE7E-F6C023A02B33",
|
||||
+ "A02E": "323EF595-AF7A-4AFA-8060-97BE72841BB9",
|
||||
+ "A02F": "45864011-CF89-46E6-A445-85262E065604",
|
||||
+ "A030": "8ED8AE95-597F-4C8A-A5BD-A7FF8E4DFAA9",
|
||||
+ "A031": "DF24E5ED-8C96-4B86-B00B-79667DC6DE11",
|
||||
+ "A032": "7C29D3AD-78B9-452E-9DEB-D098D542F092",
|
||||
+ "A033": "379D107E-229E-499D-AD4F-61F5BCF87BD4",
|
||||
+ "A034": "0DEA65E5-A676-4CDF-823C-77568B577ED5",
|
||||
+ "A035": "4627AE27-CFEF-48A1-88FE-99C3509ADE26",
|
||||
+ "A036": "20117F86-E985-4357-B9EE-374BC1D8487D",
|
||||
+ "A037": "86A7CB80-84E1-408C-99AB-694F1A410FC7",
|
||||
+ "A038": "97D7B011-54DA-4835-B3C4-917AD6E73D74",
|
||||
+ "A039": "5594C694-C871-4B5F-90B1-690A6F68E0F7",
|
||||
+ "A03A": "1B81E7E6-F50D-419B-A739-2AEEF8DA3335",
|
||||
+ "A03B": "98523EC6-90FE-4C67-B50A-0FC59ED6F56D",
|
||||
+ "A03C": "2644BCC0-F36A-4792-9533-1738BED53EE3",
|
||||
+ "A03D": "DD7C91E9-38C9-45C5-8A12-4A80F7E14057",
|
||||
+ "A03E": "7696D5B6-43FD-4664-A228-C563C4A1E8CC",
|
||||
+ "A03F": "0D802D54-058D-4A20-AD2D-C7A362CEACD4",
|
||||
+ "A040": "10A0C19C-516A-5444-5CE3-664C3226A794",
|
||||
+ "A200": "734E5AFE-F61A-11E6-BC64-92361F002671",
|
||||
+ "A500": "516E7CB4-6ECF-11D6-8FF8-00022D09712B",
|
||||
+ "A501": "83BD6B9D-7F41-11DC-BE0B-001560B84F0F",
|
||||
+ "A502": "516E7CB5-6ECF-11D6-8FF8-00022D09712B",
|
||||
+ "A503": "516E7CB6-6ECF-11D6-8FF8-00022D09712B",
|
||||
+ "A504": "516E7CBA-6ECF-11D6-8FF8-00022D09712B",
|
||||
+ "A505": "516E7CB8-6ECF-11D6-8FF8-00022D09712B",
|
||||
+ "A506": "74BA7DD9-A689-11E1-BD04-00E081286ACF",
|
||||
+ "A580": "85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7",
|
||||
+ "A581": "85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7",
|
||||
+ "A582": "85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7",
|
||||
+ "A583": "0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7",
|
||||
+ "A584": "85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7",
|
||||
+ "A585": "85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7",
|
||||
+ "A600": "824CC7A0-36A8-11E3-890A-952519AD3F61",
|
||||
+ "A800": "55465300-0000-11AA-AA11-00306543ECAC",
|
||||
+ "A900": "516E7CB4-6ECF-11D6-8FF8-00022D09712B",
|
||||
+ "A901": "49F48D32-B10E-11DC-B99B-0019D1879648",
|
||||
+ "A902": "49F48D5A-B10E-11DC-B99B-0019D1879648",
|
||||
+ "A903": "49F48D82-B10E-11DC-B99B-0019D1879648",
|
||||
+ "A904": "2DB519C4-B10F-11DC-B99B-0019D1879648",
|
||||
+ "A905": "2DB519EC-B10F-11DC-B99B-0019D1879648",
|
||||
+ "A906": "49F48DAA-B10E-11DC-B99B-0019D1879648",
|
||||
+ "AB00": "426F6F74-0000-11AA-AA11-00306543ECAC",
|
||||
+ "AF00": "48465300-0000-11AA-AA11-00306543ECAC",
|
||||
+ "AF01": "52414944-0000-11AA-AA11-00306543ECAC",
|
||||
+ "AF02": "52414944-5F4F-11AA-AA11-00306543ECAC",
|
||||
+ "AF03": "4C616265-6C00-11AA-AA11-00306543ECAC",
|
||||
+ "AF04": "5265636F-7665-11AA-AA11-00306543ECAC",
|
||||
+ "AF05": "53746F72-6167-11AA-AA11-00306543ECAC",
|
||||
+ "AF06": "B6FA30DA-92D2-4A9A-96F1-871EC6486200",
|
||||
+ "AF07": "2E313465-19B9-463F-8126-8A7993773801",
|
||||
+ "AF08": "FA709C7E-65B1-4593-BFD5-E71D61DE9B02",
|
||||
+ "AF09": "BBBA6DF5-F46F-4A89-8F59-8765B2727503",
|
||||
+ "AF0A": "7C3457EF-0000-11AA-AA11-00306543ECAC",
|
||||
+ "AF0B": "69646961-6700-11AA-AA11-00306543ECAC",
|
||||
+ "AF0C": "52637672-7900-11AA-AA11-00306543ECAC",
|
||||
+ "B000": "3DE21764-95BD-54BD-A5C3-4ABE786F38A8",
|
||||
+ "B300": "CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1",
|
||||
+ "BB00": "4778ED65-BF42-45FA-9C5B-287A1DC4AAB1",
|
||||
+ "BC00": "0311FC50-01CA-4725-AD77-9ADBB20ACE98",
|
||||
+ "BE00": "6A82CB45-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF00": "6A85CF4D-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF01": "6A898CC3-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF02": "6A87C46F-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF03": "6A8B642B-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF04": "6A8EF2E9-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF05": "6A90BA39-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF06": "6A9283A5-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF07": "6A945A3B-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF08": "6A9630D1-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF09": "6A980767-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF0A": "6A96237F-1DD2-11B2-99A6-080020736631",
|
||||
+ "BF0B": "6A8D2AC7-1DD2-11B2-99A6-080020736631",
|
||||
+ "C001": "75894C1E-3AEB-11D3-B7C1-7B03A0000000",
|
||||
+ "C002": "E2A1E728-32E3-11D6-A682-7B03A0000000",
|
||||
+ "E100": "7412F7D5-A156-4B13-81DC-867174929325",
|
||||
+ "E101": "D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149",
|
||||
+ "E900": "8C8F8EFF-AC95-4770-814A-21994F2DBC8F",
|
||||
+ "EA00": "BC13C2FF-59E6-4262-A352-B275FD6F7172",
|
||||
+ "EB00": "42465331-3BA3-10F1-802A-4861696B7521",
|
||||
+ "ED00": "F4019732-066E-4E12-8273-346C5641494F",
|
||||
+ "ED01": "BFBFAFE7-A34F-448A-9A5B-6213EB736C22",
|
||||
+ "EF00": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
|
||||
+ "EF01": "024DEE41-33E7-11D3-9D69-0008C781F39F",
|
||||
+ "EF02": "21686148-6449-6E6F-744E-656564454649",
|
||||
+ "F100": "FE8A2634-5E2E-46BA-99E3-3A192091A350",
|
||||
+ "F101": "D9FD4535-106C-4CEC-8D37-DFC020CA87CB",
|
||||
+ "F102": "A409E16B-78AA-4ACC-995C-302352621A41",
|
||||
+ "F103": "F95D940E-CABA-4578-9B93-BB6C90F29D3E",
|
||||
+ "F104": "10B8DBAA-D2BF-42A9-98C6-A7C5DB3701E7",
|
||||
+ "F105": "49FD7CB8-DF15-4E73-B9D9-992070127F0F",
|
||||
+ "F106": "421A8BFC-85D9-4D85-ACDA-B64EEC0133E9",
|
||||
+ "F107": "9B37FFF6-2E58-466A-983A-F7926D0B04E0",
|
||||
+ "F108": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
|
||||
+ "F109": "606B000B-B7C7-4653-A7D5-B737332C899D",
|
||||
+ "F10A": "08185F0C-892D-428A-A789-DBEEC8F55E6A",
|
||||
+ "F10B": "48435546-4953-2041-494E-5354414C4C52",
|
||||
+ "F10C": "2967380E-134C-4CBB-B6DA-17E7CE1CA45D",
|
||||
+ "F10D": "41D0E340-57E3-954E-8C1E-17ECAC44CFF5",
|
||||
+ "F10E": "DE30CC86-1F4A-4A31-93C4-66F147D33E05",
|
||||
+ "F10F": "23CC04DF-C278-4CE7-8471-897D1A4BCDF7",
|
||||
+ "F110": "A0E5CF57-2DEF-46BE-A80C-A2067C37CD49",
|
||||
+ "F111": "4E5E989E-4C86-11E8-A15B-480FCF35F8E6",
|
||||
+ "F112": "5A3A90BE-4C86-11E8-A15B-480FCF35F8E6",
|
||||
+ "F113": "5ECE94FE-4C86-11E8-A15B-480FCF35F8E6",
|
||||
+ "F114": "8B94D043-30BE-4871-9DFA-D69556E8C1F3",
|
||||
+ "F115": "A13B4D9A-EC5F-11E8-97D8-6C3BE52705BF",
|
||||
+ "F116": "A288ABF2-EC5F-11E8-97D8-6C3BE52705BF",
|
||||
+ "F117": "6A2460C3-CD11-4E8B-80A8-12CCE268ED0A",
|
||||
+ "F118": "1D75395D-F2C6-476B-A8B7-45CC1C97B476",
|
||||
+ "F119": "900B0FC5-90CD-4D4F-84F9-9F8ED579DB88",
|
||||
+ "F11A": "B2B2E8D1-7C10-4EBC-A2D0-4614568260AD",
|
||||
+ "F800": "4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D",
|
||||
+ "F801": "4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D",
|
||||
+ "F802": "45B0969E-9B03-4F30-B4C6-B4B80CEFF106",
|
||||
+ "F803": "45B0969E-9B03-4F30-B4C6-5EC00CEFF106",
|
||||
+ "F804": "89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE",
|
||||
+ "F805": "89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE",
|
||||
+ "F806": "CAFECAFE-9B03-4F30-B4C6-B4B80CEFF106",
|
||||
+ "F807": "30CD0809-C2B2-499C-8879-2D6B78529876",
|
||||
+ "F808": "5CE17FCE-4087-4169-B7FF-056CC58473F9",
|
||||
+ "F809": "FB3AABF9-D25F-47CC-BF5E-721D1816496B",
|
||||
+ "F80A": "4FBD7E29-8AE0-4982-BF9D-5A8D867AF560",
|
||||
+ "F80B": "45B0969E-8AE0-4982-BF9D-5A8D867AF560",
|
||||
+ "F80C": "CAFECAFE-8AE0-4982-BF9D-5A8D867AF560",
|
||||
+ "F80D": "7F4A666A-16F3-47A2-8445-152EF4D03F6C",
|
||||
+ "F80E": "EC6D6385-E346-45DC-BE91-DA2A7C8B3261",
|
||||
+ "F80F": "01B41E1B-002A-453C-9F17-88793989FF8F",
|
||||
+ "F810": "CAFECAFE-9B03-4F30-B4C6-5EC00CEFF106",
|
||||
+ "F811": "93B0052D-02D9-4D8A-A43B-33A3EE4DFBC3",
|
||||
+ "F812": "306E8683-4FE2-4330-B7C0-00A917C16966",
|
||||
+ "F813": "45B0969E-9B03-4F30-B4C6-35865CEFF106",
|
||||
+ "F814": "CAFECAFE-9B03-4F30-B4C6-35865CEFF106",
|
||||
+ "F815": "166418DA-C469-4022-ADF4-B30AFD37F176",
|
||||
+ "F816": "86A32090-3647-40B9-BBBD-38D8C573AA86",
|
||||
+ "F817": "4FBD7E29-9D25-41B8-AFD0-35865CEFF05D",
|
||||
+ "FB00": "AA31E02A-400F-11DB-9590-000C2911D1B8",
|
||||
+ "FB01": "9198EFFC-31C0-11DB-8F78-000C2911D1B8",
|
||||
+ "FC00": "9D275380-40AD-11DB-BF97-000C2911D1B8",
|
||||
+ "FD00": "A19D880F-05FC-4D3B-A006-743F0F84911E",
|
||||
+}
|
||||
+
|
||||
+gpt_id_to_sgdisk = {v: k for k, v in reversed(sgdisk_to_gpt_id.items())}
|
||||
|
||||
def check_partition_gpt_layout(device, layout):
|
||||
prt_cmd = [SGDISK_CMD, "-p", device]
|
||||
--
|
||||
2.39.3
|
||||
|
@ -1,147 +0,0 @@
|
||||
From 1a2f6a3e17aff53bcc239d66b7095ea0441b5d7b Mon Sep 17 00:00:00 2001
|
||||
From: Ani Sinha <anisinha@redhat.com>
|
||||
Date: Tue, 21 May 2024 03:04:06 +0530
|
||||
Subject: [PATCH] feat: Set RH ssh key permissions when no 'ssh_keys' group
|
||||
(#5296)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 83: feat: Set RH ssh key permissions when no 'ssh_keys' group (#5296)
|
||||
RH-Jira: RHEL-36456
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [1/1] d32c2c56227903cc00d3f807500e8cbda478b5c8 (anisinha/cloud-init)
|
||||
|
||||
Fedora core 38 and above, centos 10 stream and all distributions derived from
|
||||
them do not have the group 'ssh_keys'. Please see the fedora rawhide change
|
||||
https://src.fedoraproject.org/rpms/openssh/c/7a21555354a2c5e724aa4c287b640c24bf108780?branch=rawhide
|
||||
|
||||
In those distributions, openssh versions are 9 and above. The private
|
||||
key permissions are set as 0o600 and the public key permissions are set as
|
||||
0o644 from sshd-keygen utility. The 'root' group owns the keys.
|
||||
Please see
|
||||
https://src.fedoraproject.org/rpms/openssh/c/b615362fd0b4da657d624571441cb74983de6e3f?branch=rawhide
|
||||
|
||||
In older releases where 'ssh_keys' group is present, the private key
|
||||
permissions are set as 0o640. Public key permissions are 0o644. These
|
||||
releases have openssh version less than 9.
|
||||
|
||||
Since cloud-init generates the keys and not the sshd-genkey utility,
|
||||
permissions must be set accordingly for cloud-init generated public and
|
||||
private keys for all cases. This includes cases where 'ssh_keys' group is
|
||||
absent. This change fixes this. The code has been reworked a little
|
||||
bit so as to simplify things. Unit tests have been adjusted accordingly.
|
||||
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
(cherry picked from commit 23136e6a94821320a85117a2e4c4bb9b0926541f)
|
||||
---
|
||||
cloudinit/config/cc_ssh.py | 48 +++++++++++++++++++++------
|
||||
tests/unittests/config/test_cc_ssh.py | 15 ++++-----
|
||||
2 files changed, 45 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/config/cc_ssh.py b/cloudinit/config/cc_ssh.py
|
||||
index f69e49c1..d44e1302 100644
|
||||
--- a/cloudinit/config/cc_ssh.py
|
||||
+++ b/cloudinit/config/cc_ssh.py
|
||||
@@ -184,6 +184,42 @@ for k in GENERATE_KEY_NAMES:
|
||||
KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"'
|
||||
|
||||
|
||||
+def set_redhat_keyfile_perms(keyfile: str) -> None:
|
||||
+ """
|
||||
+ For fedora 37, centos 9 stream and below:
|
||||
+ - sshd version is earlier than version 9.
|
||||
+ - 'ssh_keys' group is present and owns the private keys.
|
||||
+ - private keys have permission 0o640.
|
||||
+ For fedora 38, centos 10 stream and above:
|
||||
+ - ssh version is atleast version 9.
|
||||
+ - 'ssh_keys' group is absent. 'root' group owns the keys.
|
||||
+ - private keys have permission 0o600, same as upstream.
|
||||
+ Public keys in all cases have permission 0o644.
|
||||
+ """
|
||||
+ permissions_public = 0o644
|
||||
+ ssh_version = ssh_util.get_opensshd_upstream_version()
|
||||
+ if ssh_version and ssh_version < util.Version(9, 0):
|
||||
+ # fedora 37, centos 9 stream and below has sshd
|
||||
+ # versions less than 9 and private key permissions are
|
||||
+ # set to 0o640 from sshd-keygen.
|
||||
+ # See sanitize permissions" section in sshd-keygen.
|
||||
+ permissions_private = 0o640
|
||||
+ else:
|
||||
+ # fedora 38, centos 10 stream and above. sshd-keygen sets
|
||||
+ # private key persmissions to 0o600.
|
||||
+ permissions_private = 0o600
|
||||
+
|
||||
+ gid = util.get_group_id("ssh_keys")
|
||||
+ if gid != -1:
|
||||
+ # 'ssh_keys' group exists for fedora 37, centos 9 stream
|
||||
+ # and below. On these distros, 'ssh_keys' group own the private
|
||||
+ # keys. When 'ssh_keys' group is absent for newer distros,
|
||||
+ # 'root' group owns the private keys which is the default.
|
||||
+ os.chown(keyfile, -1, gid)
|
||||
+ os.chmod(keyfile, permissions_private)
|
||||
+ os.chmod(f"{keyfile}.pub", permissions_public)
|
||||
+
|
||||
+
|
||||
def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
|
||||
|
||||
# remove the static keys from the pristine image
|
||||
@@ -280,16 +316,8 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None:
|
||||
):
|
||||
sys.stdout.write(util.decode_binary(out))
|
||||
|
||||
- gid = util.get_group_id("ssh_keys")
|
||||
- if gid != -1:
|
||||
- # perform same "sanitize permissions" as sshd-keygen
|
||||
- permissions_private = 0o600
|
||||
- ssh_version = ssh_util.get_opensshd_upstream_version()
|
||||
- if ssh_version and ssh_version < util.Version(9, 0):
|
||||
- permissions_private = 0o640
|
||||
- os.chown(keyfile, -1, gid)
|
||||
- os.chmod(keyfile, permissions_private)
|
||||
- os.chmod(f"{keyfile}.pub", 0o644)
|
||||
+ if cloud.distro.osfamily == "redhat":
|
||||
+ set_redhat_keyfile_perms(keyfile)
|
||||
except subp.ProcessExecutionError as e:
|
||||
err = util.decode_binary(e.stderr).lower()
|
||||
if e.exit_code == 1 and err.lower().startswith(
|
||||
diff --git a/tests/unittests/config/test_cc_ssh.py b/tests/unittests/config/test_cc_ssh.py
|
||||
index 102519eb..49327bb6 100644
|
||||
--- a/tests/unittests/config/test_cc_ssh.py
|
||||
+++ b/tests/unittests/config/test_cc_ssh.py
|
||||
@@ -307,7 +307,7 @@ class TestHandleSsh:
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ssh_keys_group_exists,sshd_version,expected_private_permissions",
|
||||
- [(False, 0, 0), (True, 8, 0o640), (True, 10, 0o600)],
|
||||
+ [(False, 9, 0o600), (True, 8, 0o640), (True, 10, 0o600)],
|
||||
)
|
||||
@mock.patch(MODPATH + "subp.subp", return_value=("", ""))
|
||||
@mock.patch(MODPATH + "util.get_group_id", return_value=10)
|
||||
@@ -336,18 +336,17 @@ class TestHandleSsh:
|
||||
m_gid.return_value = 10 if ssh_keys_group_exists else -1
|
||||
m_sshd_version.return_value = util.Version(sshd_version, 0)
|
||||
key_path = cc_ssh.KEY_FILE_TPL % "rsa"
|
||||
- cloud = get_cloud(distro="ubuntu")
|
||||
+ cloud = get_cloud(distro="centos")
|
||||
cc_ssh.handle("name", {"ssh_genkeytypes": ["rsa"]}, cloud, [])
|
||||
if ssh_keys_group_exists:
|
||||
m_chown.assert_called_once_with(key_path, -1, 10)
|
||||
- assert m_chmod.call_args_list == [
|
||||
- mock.call(key_path, expected_private_permissions),
|
||||
- mock.call(f"{key_path}.pub", 0o644),
|
||||
- ]
|
||||
else:
|
||||
- m_sshd_version.assert_not_called()
|
||||
m_chown.assert_not_called()
|
||||
- m_chmod.assert_not_called()
|
||||
+
|
||||
+ assert m_chmod.call_args_list == [
|
||||
+ mock.call(key_path, expected_private_permissions),
|
||||
+ mock.call(f"{key_path}.pub", 0o644),
|
||||
+ ]
|
||||
|
||||
@pytest.mark.parametrize("with_sshd_dconf", [False, True])
|
||||
@mock.patch(MODPATH + "util.ensure_dir")
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,207 @@
|
||||
From c21351ad9da5aebcb252aa36cbfa92ac16fa9746 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Apolloner <florian@apolloner.eu>
|
||||
Date: Fri, 5 Jan 2024 19:07:12 +0100
|
||||
Subject: [PATCH 2/3] feat: apply global DNS to interfaces in network-manager
|
||||
(#4723)
|
||||
|
||||
RH-Author: Cathy Avery <cavery@redhat.com>
|
||||
RH-MergeRequest: 72: Fixes for cloud-init fails to configure DNS/search domains for network-config v1
|
||||
RH-Jira: RHEL-20964
|
||||
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Commit: [2/2] 1d2b10133ec2558e9665f21f53e4b1a898e283a8 (cavery/cloud-init-c-9-s)
|
||||
|
||||
Sometimes DNS settings in cloud configs are specified globally and
|
||||
not per interface / subnet. This results in a configuration without
|
||||
proper nameservers. This was fixed for netplan in d29eeccd and is
|
||||
now also applied to the network-manager renderer.
|
||||
|
||||
Co-authored-by: James Falcon <james.falcon@canonical.com>
|
||||
(cherry picked from commit 0d787d0a262f70ff848b315633742aa8fc45a1de)
|
||||
Signed-off-by: Cathy Avery <cavery@redhat.com>
|
||||
---
|
||||
cloudinit/net/network_manager.py | 52 ++++++++++++++---------
|
||||
tests/unittests/net/test_net_rendering.py | 3 ++
|
||||
tests/unittests/test_net.py | 11 +++++
|
||||
tools/.github-cla-signers | 1 +
|
||||
4 files changed, 47 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
|
||||
index bd6e6d75..0ba210b7 100644
|
||||
--- a/cloudinit/net/network_manager.py
|
||||
+++ b/cloudinit/net/network_manager.py
|
||||
@@ -246,7 +246,7 @@ class NMConnection:
|
||||
"""
|
||||
return addr.replace("-", ":").upper()
|
||||
|
||||
- def render_interface(self, iface, renderer):
|
||||
+ def render_interface(self, iface, network_state, renderer):
|
||||
"""
|
||||
Integrate information from network state interface information
|
||||
into the connection. Most of the work is done here.
|
||||
@@ -311,7 +311,6 @@ class NMConnection:
|
||||
found_dns_search = []
|
||||
|
||||
# Deal with Layer 3 configuration
|
||||
- use_top_level_dns = "dns" in iface
|
||||
for subnet in iface["subnets"]:
|
||||
family = "ipv6" if subnet_is_ipv6(subnet) else "ipv4"
|
||||
|
||||
@@ -322,26 +321,39 @@ class NMConnection:
|
||||
self.config[family]["gateway"] = subnet["gateway"]
|
||||
for route in subnet["routes"]:
|
||||
self._add_route(route)
|
||||
- if not use_top_level_dns and "dns_nameservers" in subnet:
|
||||
- for nameserver in subnet["dns_nameservers"]:
|
||||
- found_nameservers.append(nameserver)
|
||||
- if not use_top_level_dns and "dns_search" in subnet:
|
||||
- found_dns_search.append(subnet["dns_search"])
|
||||
+ # Add subnet-level DNS
|
||||
+ if "dns_nameservers" in subnet:
|
||||
+ found_nameservers.extend(subnet["dns_nameservers"])
|
||||
+ if "dns_search" in subnet:
|
||||
+ found_dns_search.extend(subnet["dns_search"])
|
||||
if family == "ipv4" and "mtu" in subnet:
|
||||
ipv4_mtu = subnet["mtu"]
|
||||
|
||||
- # Now add our DNS search domains. We add them later because we
|
||||
- # only want them if an IP family has already been defined
|
||||
- if use_top_level_dns:
|
||||
- for nameserver in iface["dns"]["nameservers"]:
|
||||
- self._add_nameserver(nameserver)
|
||||
- if iface["dns"]["search"]:
|
||||
- self._add_dns_search(iface["dns"]["search"])
|
||||
- else:
|
||||
- for nameserver in found_nameservers:
|
||||
- self._add_nameserver(nameserver)
|
||||
- for dns_search in found_dns_search:
|
||||
- self._add_dns_search(dns_search)
|
||||
+ # Add interface-level DNS
|
||||
+ if "dns" in iface:
|
||||
+ found_nameservers += [
|
||||
+ dns
|
||||
+ for dns in iface["dns"]["nameservers"]
|
||||
+ if dns not in found_nameservers
|
||||
+ ]
|
||||
+ found_dns_search += [
|
||||
+ search
|
||||
+ for search in iface["dns"]["search"]
|
||||
+ if search not in found_dns_search
|
||||
+ ]
|
||||
+
|
||||
+ # We prefer any interface-specific DNS entries, but if we do not
|
||||
+ # have any, add the global DNS to the connection
|
||||
+ if not found_nameservers and network_state.dns_nameservers:
|
||||
+ found_nameservers = network_state.dns_nameservers
|
||||
+ if not found_dns_search and network_state.dns_searchdomains:
|
||||
+ found_dns_search = network_state.dns_searchdomains
|
||||
+
|
||||
+ # Write out all DNS entries to the connection
|
||||
+ for nameserver in found_nameservers:
|
||||
+ self._add_nameserver(nameserver)
|
||||
+ if found_dns_search:
|
||||
+ self._add_dns_search(found_dns_search)
|
||||
|
||||
# we do not want to set may-fail to false for both ipv4 and ipv6 dhcp
|
||||
# at the at the same time. This will make the network configuration
|
||||
@@ -457,7 +469,7 @@ class Renderer(renderer.Renderer):
|
||||
# Now render the actual interface configuration
|
||||
for iface in network_state.iter_interfaces():
|
||||
conn = self.connections[iface["name"]]
|
||||
- conn.render_interface(iface, self)
|
||||
+ conn.render_interface(iface, network_state, self)
|
||||
|
||||
# And finally write the files
|
||||
for con_id, conn in self.connections.items():
|
||||
diff --git a/tests/unittests/net/test_net_rendering.py b/tests/unittests/net/test_net_rendering.py
|
||||
index 06feab89..f340ffc1 100644
|
||||
--- a/tests/unittests/net/test_net_rendering.py
|
||||
+++ b/tests/unittests/net/test_net_rendering.py
|
||||
@@ -88,6 +88,9 @@ def _check_network_manager(network_state: NetworkState, tmp_path: Path):
|
||||
"test_name, renderers",
|
||||
[("no_matching_mac_v2", Renderer.Netplan | Renderer.NetworkManager)],
|
||||
)
|
||||
+@pytest.mark.xfail(
|
||||
+ reason="v2 interface-specific DNS errantly gets applied globally"
|
||||
+)
|
||||
def test_convert(test_name, renderers, tmp_path):
|
||||
network_config = safeyaml.load(
|
||||
Path(ARTIFACT_DIR, f"{test_name}.yaml").read_text()
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index 2a99f150..d7c9a414 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -646,6 +646,7 @@ method=manual
|
||||
may-fail=false
|
||||
address1=172.19.1.34/22
|
||||
route1=0.0.0.0/0,172.19.3.254
|
||||
+dns=172.19.0.12;
|
||||
|
||||
""".lstrip(),
|
||||
),
|
||||
@@ -2797,6 +2798,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
|
||||
[ipv4]
|
||||
method=auto
|
||||
may-fail=false
|
||||
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
|
||||
+ dns-search=barley.maas;wark.maas;foobar.maas;
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -2822,6 +2825,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
|
||||
method=manual
|
||||
may-fail=false
|
||||
address1=192.168.200.7/24
|
||||
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
|
||||
+ dns-search=barley.maas;wark.maas;foobar.maas;
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -2846,6 +2851,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
|
||||
[ipv4]
|
||||
method=auto
|
||||
may-fail=false
|
||||
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
|
||||
+ dns-search=barley.maas;wark.maas;foobar.maas;
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -2930,12 +2937,15 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
|
||||
method=manual
|
||||
may-fail=false
|
||||
address1=192.168.14.2/24
|
||||
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
|
||||
+ dns-search=barley.maas;wark.maas;foobar.maas;
|
||||
|
||||
[ipv6]
|
||||
method=manual
|
||||
may-fail=false
|
||||
address1=2001:1::1/64
|
||||
route1=::/0,2001:4800:78ff:1b::1
|
||||
+ dns-search=barley.maas;wark.maas;foobar.maas;
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -2990,6 +3000,7 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
|
||||
[ipv6]
|
||||
method=auto
|
||||
may-fail=false
|
||||
+ dns-search=barley.maas;wark.maas;foobar.maas;
|
||||
|
||||
"""
|
||||
),
|
||||
diff --git a/tools/.github-cla-signers b/tools/.github-cla-signers
|
||||
index dbdb9cfa..f4da0989 100644
|
||||
--- a/tools/.github-cla-signers
|
||||
+++ b/tools/.github-cla-signers
|
||||
@@ -13,6 +13,7 @@ andrewbogott
|
||||
andrewlukoshko
|
||||
ani-sinha
|
||||
antonyc
|
||||
+apollo13
|
||||
aswinrajamannar
|
||||
bdrung
|
||||
beantaxi
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,110 @@
|
||||
From 2f7f3dc6237ea70825dcb70f71d9718f631a9d95 Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Tue, 6 Feb 2024 09:24:37 -0600
|
||||
Subject: [PATCH] fix: Add types to network v1 schema (#4841)
|
||||
|
||||
RH-Author: Cathy Avery <cavery@redhat.com>
|
||||
RH-MergeRequest: 69: fix: Add types to network v1 schema (#4841)
|
||||
RH-Jira: RHEL-21324
|
||||
RH-Acked-by: Ani Sinha <None>
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Commit: [1/1] 59b2b4b07dd9eed956943a22b90af487f18b4cbd (cavery/cloud-init-c-9-s)
|
||||
|
||||
Conflicts:
|
||||
No log argument as we are not including commit e168b4a1383b6eae9c1dc81411d7684fcbbf7df9
|
||||
|
||||
Even though it has conflicted with our documentation, we have allowed
|
||||
nameserver address to a be a string, mtu to be empty, and nameserver
|
||||
search to be missing. Since we have allowed these, expand our schema
|
||||
and documentation accordingly.
|
||||
|
||||
Fixes GH-4710
|
||||
|
||||
(cherry picked from commit b08193b376552ede5d162d8283310adc783d81bf)
|
||||
Signed-off-by: Cathy Avery <cavery@redhat.com>
|
||||
---
|
||||
.../config/schemas/schema-network-config-v1.json | 13 +++++++++----
|
||||
doc/rtd/reference/network-config-format-v1.rst | 4 ++--
|
||||
tests/unittests/config/test_schema.py | 13 +++++++++++++
|
||||
3 files changed, 24 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/config/schemas/schema-network-config-v1.json b/cloudinit/config/schemas/schema-network-config-v1.json
|
||||
index c77885ec..56dc27c9 100644
|
||||
--- a/cloudinit/config/schemas/schema-network-config-v1.json
|
||||
+++ b/cloudinit/config/schemas/schema-network-config-v1.json
|
||||
@@ -24,7 +24,10 @@
|
||||
"description": "The lowercase MAC address of the physical device."
|
||||
},
|
||||
"mtu": {
|
||||
- "type": "integer",
|
||||
+ "type": [
|
||||
+ "integer",
|
||||
+ "null"
|
||||
+ ],
|
||||
"description": "The MTU size in bytes. The ``mtu`` key represents a device's Maximum Transmission Unit, which is the largest size packet or frame, specified in octets (eight-bit bytes), that can be sent in a packet- or frame-based network. Specifying ``mtu`` is optional. Values too small or too large for a device may be ignored by that device."
|
||||
},
|
||||
"subnets": {
|
||||
@@ -384,8 +387,7 @@
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"type",
|
||||
- "address",
|
||||
- "search"
|
||||
+ "address"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -396,7 +398,10 @@
|
||||
},
|
||||
"address": {
|
||||
"description": "List of IPv4 or IPv6 address of nameservers.",
|
||||
- "type": "array",
|
||||
+ "type": [
|
||||
+ "array",
|
||||
+ "string"
|
||||
+ ],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
diff --git a/doc/rtd/reference/network-config-format-v1.rst b/doc/rtd/reference/network-config-format-v1.rst
|
||||
index d267eb94..42f2dc22 100644
|
||||
--- a/doc/rtd/reference/network-config-format-v1.rst
|
||||
+++ b/doc/rtd/reference/network-config-format-v1.rst
|
||||
@@ -252,8 +252,8 @@ Users can specify a ``nameserver`` type. Nameserver dictionaries include
|
||||
the following keys:
|
||||
|
||||
- ``address``: List of IPv4 or IPv6 address of nameservers.
|
||||
-- ``search``: List of hostnames to include in the :file:`resolv.conf` search
|
||||
- path.
|
||||
+- ``search``: Optional. List of hostnames to include in the :file:`resolv.conf`
|
||||
+ search path.
|
||||
- ``interface``: Optional. Ties the nameserver definition to the specified
|
||||
interface. The value specified here must match the ``name`` of an interface
|
||||
defined in this config. If unspecified, this nameserver will be considered
|
||||
diff --git a/tests/unittests/config/test_schema.py b/tests/unittests/config/test_schema.py
|
||||
index 28f0b39d..52667332 100644
|
||||
--- a/tests/unittests/config/test_schema.py
|
||||
+++ b/tests/unittests/config/test_schema.py
|
||||
@@ -2048,6 +2048,19 @@ class TestNetworkSchema:
|
||||
does_not_raise(),
|
||||
id="bond_with_all_known_properties",
|
||||
),
|
||||
+ pytest.param(
|
||||
+ {
|
||||
+ "network": {
|
||||
+ "version": 1,
|
||||
+ "config": [
|
||||
+ {"type": "physical", "name": "eth0", "mtu": None},
|
||||
+ {"type": "nameserver", "address": "8.8.8.8"},
|
||||
+ ],
|
||||
+ }
|
||||
+ },
|
||||
+ does_not_raise(),
|
||||
+ id="GH-4710_mtu_none_and_str_address",
|
||||
+ ),
|
||||
),
|
||||
)
|
||||
def test_network_schema(self, src_config, expectation):
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,73 @@
|
||||
From 8ead44cb39f7726a695aa21a34820f6d40270829 Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Mon, 12 Feb 2024 14:48:01 -0600
|
||||
Subject: [PATCH 5/6] fix: Address TIOBE abstract interpretation issues (#4866)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 92: Update pylint version to support python 3.12
|
||||
RH-Jira: RHEL-44598
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [2/3] 3ca11206fa159ab45b2db21e78c4cfaf358b1e01 (anisinha/cloud-init)
|
||||
|
||||
These involve operations on possibly null variables or impossible logic.
|
||||
|
||||
(cherry picked from commit 5e7ef1032a12267a9a518358fbf89da0a88ddb99)
|
||||
---
|
||||
cloudinit/config/cc_lxd.py | 2 +-
|
||||
cloudinit/distros/parsers/ifconfig.py | 6 ++++++
|
||||
cloudinit/util.py | 1 +
|
||||
3 files changed, 8 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py
|
||||
index cb9fc4f3..9f267b4c 100644
|
||||
--- a/cloudinit/config/cc_lxd.py
|
||||
+++ b/cloudinit/config/cc_lxd.py
|
||||
@@ -432,7 +432,7 @@ def bridge_to_cmd(bridge_cfg):
|
||||
% (bridge_cfg.get("ipv6_address"), bridge_cfg.get("ipv6_netmask"))
|
||||
)
|
||||
|
||||
- if bridge_cfg.get("ipv6_nat", "false") == "true":
|
||||
+ if bridge_cfg.get("ipv6_nat") == "true":
|
||||
cmd_create.append("ipv6.nat=true")
|
||||
|
||||
else:
|
||||
diff --git a/cloudinit/distros/parsers/ifconfig.py b/cloudinit/distros/parsers/ifconfig.py
|
||||
index 516b5eb5..d671df1f 100644
|
||||
--- a/cloudinit/distros/parsers/ifconfig.py
|
||||
+++ b/cloudinit/distros/parsers/ifconfig.py
|
||||
@@ -102,6 +102,7 @@ class Ifconfig:
|
||||
"""
|
||||
ifindex = 0
|
||||
ifs_by_mac = defaultdict(list)
|
||||
+ dev = None
|
||||
for line in text.splitlines():
|
||||
if len(line) == 0:
|
||||
continue
|
||||
@@ -119,6 +120,11 @@ class Ifconfig:
|
||||
dev.index = ifindex
|
||||
self._ifs_by_name[curif] = dev
|
||||
|
||||
+ if not dev:
|
||||
+ # This shouldn't happen with normal ifconfig output, but
|
||||
+ # if it does, ensure we don't Traceback
|
||||
+ continue
|
||||
+
|
||||
toks = line.lower().strip().split()
|
||||
|
||||
if len(toks) > 1 and toks[1].startswith("flags="):
|
||||
diff --git a/cloudinit/util.py b/cloudinit/util.py
|
||||
index 3295735c..5f787c5c 100644
|
||||
--- a/cloudinit/util.py
|
||||
+++ b/cloudinit/util.py
|
||||
@@ -1417,6 +1417,7 @@ def find_devs_with_netbsd(
|
||||
devlist = []
|
||||
label = None
|
||||
_type = None
|
||||
+ mscdlabel_out = ""
|
||||
if criteria:
|
||||
if criteria.startswith("LABEL="):
|
||||
label = criteria.lstrip("LABEL=")
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,66 @@
|
||||
From 62cec1e38e117fe6b24888862576ac57be14bbda Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Tue, 26 Mar 2024 15:55:50 -0500
|
||||
Subject: [PATCH] fix: Always use single datasource if specified (#5098)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 82: fix: Always use single datasource if specified (#5098)
|
||||
RH-Jira: RHEL-36255
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] 068e97fcc18dd99f1112a9109acdb30fe2880f6e (anisinha/cloud-init)
|
||||
|
||||
This change may require a user to add `None` to the `datasource_list`
|
||||
defined in `/etc/cloud/cloud.cfg[.d]` if they have a customized
|
||||
datasource_list and want the DataSourceNone fallback behavior.
|
||||
|
||||
ds-identify would automatically append "None" to the datasource_list
|
||||
if a single entry was provided in /etc/cloud/cloud.cfg[.d].
|
||||
This wasn't a problem in the past as the python code would detect
|
||||
a single datasource along with None as an indication to automatically
|
||||
use that datasource. Since the python code no longer does that,
|
||||
we should ensure that one specified datasource results in one specified
|
||||
datasource after ds-identify has run.
|
||||
|
||||
Fixes GH-5091
|
||||
|
||||
(cherry picked from commit cdbbd17ae400e432d13f674c18a6f5c873fa328b)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
tests/unittests/test_ds_identify.py | 2 +-
|
||||
tools/ds-identify | 6 +++++-
|
||||
2 files changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
|
||||
index ba0bf779..acbf3f03 100644
|
||||
--- a/tests/unittests/test_ds_identify.py
|
||||
+++ b/tests/unittests/test_ds_identify.py
|
||||
@@ -522,7 +522,7 @@ class TestDsIdentify(DsIdentifyBase):
|
||||
mydata = copy.deepcopy(VALID_CFG["Ec2-hvm"])
|
||||
cfgpath = "etc/cloud/cloud.cfg.d/myds.cfg"
|
||||
mydata["files"][cfgpath] = 'datasource_list: ["NoCloud"]\n'
|
||||
- self._check_via_dict(mydata, rc=RC_FOUND, dslist=["NoCloud", DS_NONE])
|
||||
+ self._check_via_dict(mydata, rc=RC_FOUND, dslist=["NoCloud"])
|
||||
|
||||
def test_configured_list_with_none(self):
|
||||
"""When datasource_list already contains None, None is not added.
|
||||
diff --git a/tools/ds-identify b/tools/ds-identify
|
||||
index ec2cc18a..6e49ded3 100755
|
||||
--- a/tools/ds-identify
|
||||
+++ b/tools/ds-identify
|
||||
@@ -1865,7 +1865,11 @@ _main() {
|
||||
# if there is only a single entry in $DI_DSLIST
|
||||
if [ $# -eq 1 ] || [ $# -eq 2 -a "$2" = "None" ] ; then
|
||||
debug 1 "single entry in datasource_list ($DI_DSLIST) use that."
|
||||
- found "$@"
|
||||
+ if [ $# -eq 1 ]; then
|
||||
+ write_result "datasource_list: [ $1 ]"
|
||||
+ else
|
||||
+ found "$@"
|
||||
+ fi
|
||||
return
|
||||
fi
|
||||
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,391 @@
|
||||
From aaf1d063f198ce09f0d539a85e1a1a2bb834520b Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Tue, 2 Jan 2024 11:29:17 -0600
|
||||
Subject: [PATCH 1/3] fix: Correct v2 NetworkManager route rendering (#4637)
|
||||
|
||||
RH-Author: Cathy Avery <cavery@redhat.com>
|
||||
RH-MergeRequest: 72: Fixes for cloud-init fails to configure DNS/search domains for network-config v1
|
||||
RH-Jira: RHEL-20964
|
||||
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Commit: [1/2] fb865987dbcf506a674eb9798f9c06859539a696 (cavery/cloud-init-c-9-s)
|
||||
|
||||
fix: Correct v2 NetworkManager route rendering
|
||||
|
||||
Because network v2 route defintions can have mixed v4 and v6 routes, we
|
||||
need to determine the IP family per route rather than per subnet.
|
||||
|
||||
Similar, ensure dns-search is rendered correctly.
|
||||
|
||||
Fixes GH-4518
|
||||
|
||||
(cherry picked from commit c2c100e8c9fd8709539b3ab2b0ee34c66ba3f2f7)
|
||||
Signed-off-by: Cathy Avery <cavery@redhat.com>
|
||||
---
|
||||
cloudinit/net/__init__.py | 2 +
|
||||
cloudinit/net/network_manager.py | 87 +++++++++-------
|
||||
tests/unittests/test_net.py | 165 ++++++++++++++++++++++++++++++-
|
||||
3 files changed, 219 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
|
||||
index c0888f52..65e7ff33 100644
|
||||
--- a/cloudinit/net/__init__.py
|
||||
+++ b/cloudinit/net/__init__.py
|
||||
@@ -1287,6 +1287,8 @@ def subnet_is_ipv6(subnet) -> bool:
|
||||
"""Common helper for checking network_state subnets for ipv6."""
|
||||
# 'static6', 'dhcp6', 'ipv6_dhcpv6-stateful', 'ipv6_dhcpv6-stateless' or
|
||||
# 'ipv6_slaac'
|
||||
+ # This function is inappropriate for v2-based routes as routes defined
|
||||
+ # under v2 subnets can contain ipv4 and ipv6 simultaneously
|
||||
if subnet["type"].endswith("6") or subnet["type"] in IPV6_DYNAMIC_TYPES:
|
||||
# This is a request either static6 type or DHCPv6.
|
||||
return True
|
||||
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
|
||||
index 76a0ac15..bd6e6d75 100644
|
||||
--- a/cloudinit/net/network_manager.py
|
||||
+++ b/cloudinit/net/network_manager.py
|
||||
@@ -12,10 +12,15 @@ import itertools
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
-from typing import Optional
|
||||
+from typing import List, Optional
|
||||
|
||||
from cloudinit import subp, util
|
||||
-from cloudinit.net import is_ipv6_address, renderer, subnet_is_ipv6
|
||||
+from cloudinit.net import (
|
||||
+ is_ipv6_address,
|
||||
+ is_ipv6_network,
|
||||
+ renderer,
|
||||
+ subnet_is_ipv6,
|
||||
+)
|
||||
from cloudinit.net.network_state import NetworkState
|
||||
from cloudinit.net.sysconfig import available_nm_ifcfg_rh
|
||||
|
||||
@@ -158,11 +163,11 @@ class NMConnection:
|
||||
if self.config[family]["method"] == "auto" and method == "manual":
|
||||
return
|
||||
|
||||
- if (
|
||||
- subnet_type == "ipv6_dhcpv6-stateful"
|
||||
- or subnet_type == "ipv6_dhcpv6-stateless"
|
||||
- or subnet_type == "ipv6_slaac"
|
||||
- ):
|
||||
+ if subnet_type in [
|
||||
+ "ipv6_dhcpv6-stateful",
|
||||
+ "ipv6_dhcpv6-stateless",
|
||||
+ "ipv6_slaac",
|
||||
+ ]:
|
||||
# set ipv4 method to 'disabled' to align with sysconfig renderer.
|
||||
self._set_default("ipv4", "method", "disabled")
|
||||
|
||||
@@ -174,7 +179,8 @@ class NMConnection:
|
||||
Adds a numbered property, such as address<n> or route<n>, ensuring
|
||||
the appropriate value gets used for <n>.
|
||||
"""
|
||||
-
|
||||
+ if not self.config.has_section(section):
|
||||
+ self.config[section] = {}
|
||||
for index in itertools.count(1):
|
||||
key = f"{key_prefix}{index}"
|
||||
if not self.config.has_option(section, key):
|
||||
@@ -189,40 +195,37 @@ class NMConnection:
|
||||
value = subnet["address"] + "/" + str(subnet["prefix"])
|
||||
self._add_numbered(family, "address", value)
|
||||
|
||||
- def _add_route(self, family, route):
|
||||
- """
|
||||
- Adds a ipv[46].route<n> property.
|
||||
- """
|
||||
-
|
||||
+ def _add_route(self, route):
|
||||
+ """Adds a ipv[46].route<n> property."""
|
||||
+ # Because network v2 route definitions can have mixed v4 and v6
|
||||
+ # routes, determine the family per route based on the gateway
|
||||
+ family = "ipv6" if is_ipv6_network(route["gateway"]) else "ipv4"
|
||||
value = route["network"] + "/" + str(route["prefix"])
|
||||
if "gateway" in route:
|
||||
value = value + "," + route["gateway"]
|
||||
self._add_numbered(family, "route", value)
|
||||
|
||||
- def _add_nameserver(self, dns):
|
||||
+ def _add_nameserver(self, dns: str) -> None:
|
||||
"""
|
||||
Extends the ipv[46].dns property with a name server.
|
||||
"""
|
||||
-
|
||||
- # FIXME: the subnet contains IPv4 and IPv6 name server mixed
|
||||
- # together. We might be getting an IPv6 name server while
|
||||
- # we're dealing with an IPv4 subnet. Sort this out by figuring
|
||||
- # out the correct family and making sure a valid section exist.
|
||||
family = "ipv6" if is_ipv6_address(dns) else "ipv4"
|
||||
- self._set_default(family, "method", "disabled")
|
||||
-
|
||||
- self._set_default(family, "dns", "")
|
||||
- self.config[family]["dns"] = self.config[family]["dns"] + dns + ";"
|
||||
+ if self.config.has_section(family):
|
||||
+ self._set_default(family, "dns", "")
|
||||
+ self.config[family]["dns"] = self.config[family]["dns"] + dns + ";"
|
||||
|
||||
- def _add_dns_search(self, family, dns_search):
|
||||
+ def _add_dns_search(self, dns_search: List[str]) -> None:
|
||||
"""
|
||||
Extends the ipv[46].dns-search property with a name server.
|
||||
"""
|
||||
-
|
||||
- self._set_default(family, "dns-search", "")
|
||||
- self.config[family]["dns-search"] = (
|
||||
- self.config[family]["dns-search"] + ";".join(dns_search) + ";"
|
||||
- )
|
||||
+ for family in ["ipv4", "ipv6"]:
|
||||
+ if self.config.has_section(family):
|
||||
+ self._set_default(family, "dns-search", "")
|
||||
+ self.config[family]["dns-search"] = (
|
||||
+ self.config[family]["dns-search"]
|
||||
+ + ";".join(dns_search)
|
||||
+ + ";"
|
||||
+ )
|
||||
|
||||
def con_uuid(self):
|
||||
"""
|
||||
@@ -304,8 +307,11 @@ class NMConnection:
|
||||
|
||||
device_mtu = iface["mtu"]
|
||||
ipv4_mtu = None
|
||||
+ found_nameservers = []
|
||||
+ found_dns_search = []
|
||||
|
||||
# Deal with Layer 3 configuration
|
||||
+ use_top_level_dns = "dns" in iface
|
||||
for subnet in iface["subnets"]:
|
||||
family = "ipv6" if subnet_is_ipv6(subnet) else "ipv4"
|
||||
|
||||
@@ -315,15 +321,28 @@ class NMConnection:
|
||||
if "gateway" in subnet:
|
||||
self.config[family]["gateway"] = subnet["gateway"]
|
||||
for route in subnet["routes"]:
|
||||
- self._add_route(family, route)
|
||||
- if "dns_nameservers" in subnet:
|
||||
+ self._add_route(route)
|
||||
+ if not use_top_level_dns and "dns_nameservers" in subnet:
|
||||
for nameserver in subnet["dns_nameservers"]:
|
||||
- self._add_nameserver(nameserver)
|
||||
- if "dns_search" in subnet:
|
||||
- self._add_dns_search(family, subnet["dns_search"])
|
||||
+ found_nameservers.append(nameserver)
|
||||
+ if not use_top_level_dns and "dns_search" in subnet:
|
||||
+ found_dns_search.append(subnet["dns_search"])
|
||||
if family == "ipv4" and "mtu" in subnet:
|
||||
ipv4_mtu = subnet["mtu"]
|
||||
|
||||
+ # Now add our DNS search domains. We add them later because we
|
||||
+ # only want them if an IP family has already been defined
|
||||
+ if use_top_level_dns:
|
||||
+ for nameserver in iface["dns"]["nameservers"]:
|
||||
+ self._add_nameserver(nameserver)
|
||||
+ if iface["dns"]["search"]:
|
||||
+ self._add_dns_search(iface["dns"]["search"])
|
||||
+ else:
|
||||
+ for nameserver in found_nameservers:
|
||||
+ self._add_nameserver(nameserver)
|
||||
+ for dns_search in found_dns_search:
|
||||
+ self._add_dns_search(dns_search)
|
||||
+
|
||||
# we do not want to set may-fail to false for both ipv4 and ipv6 dhcp
|
||||
# at the at the same time. This will make the network configuration
|
||||
# work only when both ipv4 and ipv6 dhcp succeeds. This may not be
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index d9ef493b..2a99f150 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -2962,9 +2962,9 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
|
||||
may-fail=false
|
||||
address1=192.168.0.2/24
|
||||
gateway=192.168.0.1
|
||||
+ address2=192.168.2.10/24
|
||||
dns=192.168.0.10;10.23.23.134;
|
||||
dns-search=barley.maas;sacchromyces.maas;brettanomyces.maas;
|
||||
- address2=192.168.2.10/24
|
||||
|
||||
"""
|
||||
),
|
||||
@@ -4154,6 +4154,148 @@ iface bond0 inet6 static
|
||||
"""
|
||||
),
|
||||
},
|
||||
+ "v2-mixed-routes": {
|
||||
+ "expected_network_manager": {
|
||||
+ "cloud-init-eth0.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init eth0
|
||||
+ uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
|
||||
+ autoconnect-priority=120
|
||||
+ type=ethernet
|
||||
+ interface-name=eth0
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [ethernet]
|
||||
+
|
||||
+ [ipv4]
|
||||
+ method=auto
|
||||
+ may-fail=true
|
||||
+ route1=169.254.42.42/32,62.210.0.1
|
||||
+ route2=169.254.42.43/32,62.210.0.2
|
||||
+ address1=192.168.1.20/16
|
||||
+ dns=8.8.8.8;
|
||||
+ dns-search=lab;home;
|
||||
+
|
||||
+ [ipv6]
|
||||
+ route1=::/0,fe80::dc00:ff:fe20:186
|
||||
+ route2=fe80::dc00:ff:fe20:188/64,fe80::dc00:ff:fe20:187
|
||||
+ method=auto
|
||||
+ may-fail=true
|
||||
+ address1=2001:bc8:1210:232:dc00:ff:fe20:185/64
|
||||
+ dns=FEDC::1;
|
||||
+ dns-search=lab;home;
|
||||
+
|
||||
+ """
|
||||
+ )
|
||||
+ },
|
||||
+ "yaml": textwrap.dedent(
|
||||
+ """\
|
||||
+ version: 2
|
||||
+ ethernets:
|
||||
+ eth0:
|
||||
+ dhcp4: true
|
||||
+ dhcp6: true
|
||||
+ nameservers:
|
||||
+ search: [lab, home]
|
||||
+ addresses: [8.8.8.8, "FEDC::1"]
|
||||
+ routes:
|
||||
+ - to: 169.254.42.42/32
|
||||
+ via: 62.210.0.1
|
||||
+ - via: fe80::dc00:ff:fe20:186
|
||||
+ to: ::/0
|
||||
+ - to: 169.254.42.43/32
|
||||
+ via: 62.210.0.2
|
||||
+ - via: fe80::dc00:ff:fe20:187
|
||||
+ to: fe80::dc00:ff:fe20:188
|
||||
+ addresses:
|
||||
+ - 192.168.1.20/16
|
||||
+ - 2001:bc8:1210:232:dc00:ff:fe20:185/64
|
||||
+ """
|
||||
+ ),
|
||||
+ },
|
||||
+ "v2-dns-no-if-ips": {
|
||||
+ "expected_network_manager": {
|
||||
+ "cloud-init-eth0.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init eth0
|
||||
+ uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
|
||||
+ autoconnect-priority=120
|
||||
+ type=ethernet
|
||||
+ interface-name=eth0
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [ethernet]
|
||||
+
|
||||
+ [ipv4]
|
||||
+ method=auto
|
||||
+ may-fail=true
|
||||
+ dns=8.8.8.8;
|
||||
+ dns-search=lab;home;
|
||||
+
|
||||
+ [ipv6]
|
||||
+ method=auto
|
||||
+ may-fail=true
|
||||
+ dns=FEDC::1;
|
||||
+ dns-search=lab;home;
|
||||
+
|
||||
+ """
|
||||
+ )
|
||||
+ },
|
||||
+ "yaml": textwrap.dedent(
|
||||
+ """\
|
||||
+ version: 2
|
||||
+ ethernets:
|
||||
+ eth0:
|
||||
+ dhcp4: true
|
||||
+ dhcp6: true
|
||||
+ nameservers:
|
||||
+ search: [lab, home]
|
||||
+ addresses: [8.8.8.8, "FEDC::1"]
|
||||
+ """
|
||||
+ ),
|
||||
+ },
|
||||
+ "v2-dns-no-dhcp": {
|
||||
+ "expected_network_manager": {
|
||||
+ "cloud-init-eth0.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init eth0
|
||||
+ uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
|
||||
+ autoconnect-priority=120
|
||||
+ type=ethernet
|
||||
+ interface-name=eth0
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [ethernet]
|
||||
+
|
||||
+ """
|
||||
+ )
|
||||
+ },
|
||||
+ "yaml": textwrap.dedent(
|
||||
+ """\
|
||||
+ version: 2
|
||||
+ ethernets:
|
||||
+ eth0:
|
||||
+ nameservers:
|
||||
+ search: [lab, home]
|
||||
+ addresses: [8.8.8.8, "FEDC::1"]
|
||||
+ """
|
||||
+ ),
|
||||
+ },
|
||||
}
|
||||
|
||||
|
||||
@@ -6267,6 +6409,27 @@ class TestNetworkManagerRendering(CiTestCase):
|
||||
entry[self.expected_name], self.expected_conf_d, found
|
||||
)
|
||||
|
||||
+ def test_v2_mixed_routes(self):
|
||||
+ entry = NETWORK_CONFIGS["v2-mixed-routes"]
|
||||
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
||||
+ self._compare_files_to_expected(
|
||||
+ entry[self.expected_name], self.expected_conf_d, found
|
||||
+ )
|
||||
+
|
||||
+ def test_v2_dns_no_ips(self):
|
||||
+ entry = NETWORK_CONFIGS["v2-dns-no-if-ips"]
|
||||
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
||||
+ self._compare_files_to_expected(
|
||||
+ entry[self.expected_name], self.expected_conf_d, found
|
||||
+ )
|
||||
+
|
||||
+ def test_v2_dns_no_dhcp(self):
|
||||
+ entry = NETWORK_CONFIGS["v2-dns-no-dhcp"]
|
||||
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
||||
+ self._compare_files_to_expected(
|
||||
+ entry[self.expected_name], self.expected_conf_d, found
|
||||
+ )
|
||||
+
|
||||
|
||||
@mock.patch(
|
||||
"cloudinit.net.is_openvswitch_internal_interface",
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,306 @@
|
||||
From 1df31428c87f08c790c300ba402318378cea8d65 Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Thu, 10 Oct 2024 23:19:28 -0500
|
||||
Subject: [PATCH 1/2] fix: Render bridges correctly for v2 on sysconfig with
|
||||
set-name (#5674)
|
||||
|
||||
RH-Author: xiachen <xiachen@redhat.com>
|
||||
RH-MergeRequest: 147: fix: Render bridges correctly for v2 on sysconfig with set-name (#5674)
|
||||
RH-Jira: RHEL-65021
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
|
||||
RH-Commit: [1/2] 7f7fce173ee9a3ba719fc36580fbce813c5bfbd0 (xiachen/cloud-init)
|
||||
|
||||
When listing interfaces in v2 format, we should expect to be able to
|
||||
reference other interfaces using the name in the configuration, not
|
||||
the name the interface will eventually take. This was broken when
|
||||
using `set-name`.
|
||||
|
||||
To fix this, we now store the configuration id alongside the eventual
|
||||
name, and reference that instead of the name.
|
||||
|
||||
Fixes GH-5574
|
||||
|
||||
(cherry picked from commit a8f69409e5cebf43767d3bb54fbad7ced1e8fc7b)
|
||||
Signed-off-by: Amy Chen <xiachen@redhat.com>
|
||||
---
|
||||
cloudinit/net/eni.py | 6 +++
|
||||
cloudinit/net/network_state.py | 30 ++++-------
|
||||
cloudinit/net/sysconfig.py | 24 ++++++---
|
||||
tests/unittests/test_net.py | 96 ++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 128 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
|
||||
index 486fa22dc..ac0306d6a 100644
|
||||
--- a/cloudinit/net/eni.py
|
||||
+++ b/cloudinit/net/eni.py
|
||||
@@ -5,6 +5,7 @@ import glob
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
+from contextlib import suppress
|
||||
from typing import Optional
|
||||
|
||||
from cloudinit import subp, util
|
||||
@@ -421,6 +422,11 @@ class Renderer(renderer.Renderer):
|
||||
return content
|
||||
|
||||
def _render_iface(self, iface, render_hwaddress=False):
|
||||
+ iface = copy.deepcopy(iface)
|
||||
+
|
||||
+ # Remove irrelevant keys
|
||||
+ with suppress(KeyError):
|
||||
+ iface.pop("config_id")
|
||||
sections = []
|
||||
subnets = iface.get("subnets", {})
|
||||
accept_ra = iface.pop("accept-ra", None)
|
||||
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
|
||||
index 14c57cdcc..226421bd0 100644
|
||||
--- a/cloudinit/net/network_state.py
|
||||
+++ b/cloudinit/net/network_state.py
|
||||
@@ -411,6 +411,7 @@ class NetworkStateInterpreter:
|
||||
wakeonlan = util.is_true(wakeonlan)
|
||||
iface.update(
|
||||
{
|
||||
+ "config_id": command.get("config_id"),
|
||||
"name": command.get("name"),
|
||||
"type": command.get("type"),
|
||||
"mac_address": command.get("mac_address"),
|
||||
@@ -424,7 +425,8 @@ class NetworkStateInterpreter:
|
||||
"wakeonlan": wakeonlan,
|
||||
}
|
||||
)
|
||||
- self._network_state["interfaces"].update({command.get("name"): iface})
|
||||
+ iface_key = command.get("config_id", command.get("name"))
|
||||
+ self._network_state["interfaces"].update({iface_key: iface})
|
||||
self.dump_network_state()
|
||||
|
||||
@ensure_command_keys(["name", "vlan_id", "vlan_link"])
|
||||
@@ -712,6 +714,7 @@ class NetworkStateInterpreter:
|
||||
|
||||
for eth, cfg in command.items():
|
||||
phy_cmd = {
|
||||
+ "config_id": eth,
|
||||
"type": "physical",
|
||||
}
|
||||
match = cfg.get("match", {})
|
||||
@@ -800,28 +803,15 @@ class NetworkStateInterpreter:
|
||||
def _v2_common(self, cfg) -> None:
|
||||
LOG.debug("v2_common: handling config:\n%s", cfg)
|
||||
for iface, dev_cfg in cfg.items():
|
||||
- if "set-name" in dev_cfg:
|
||||
- set_name_iface = dev_cfg.get("set-name")
|
||||
- if set_name_iface:
|
||||
- iface = set_name_iface
|
||||
if "nameservers" in dev_cfg:
|
||||
- search = dev_cfg.get("nameservers").get("search", [])
|
||||
- dns = dev_cfg.get("nameservers").get("addresses", [])
|
||||
+ search = dev_cfg.get("nameservers").get("search")
|
||||
+ dns = dev_cfg.get("nameservers").get("addresses")
|
||||
name_cmd = {"type": "nameserver"}
|
||||
- if len(search) > 0:
|
||||
- name_cmd.update({"search": search})
|
||||
- if len(dns) > 0:
|
||||
- name_cmd.update({"address": dns})
|
||||
+ if search:
|
||||
+ name_cmd["search"] = search
|
||||
+ if dns:
|
||||
+ name_cmd["address"] = dns
|
||||
self.handle_nameserver(name_cmd)
|
||||
-
|
||||
- mac_address: Optional[str] = dev_cfg.get("match", {}).get(
|
||||
- "macaddress"
|
||||
- )
|
||||
- if mac_address:
|
||||
- real_if_name = find_interface_name_from_mac(mac_address)
|
||||
- if real_if_name:
|
||||
- iface = real_if_name
|
||||
-
|
||||
self._handle_individual_nameserver(name_cmd, iface)
|
||||
|
||||
def _handle_bond_bridge(self, command, cmd_type=None):
|
||||
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
||||
index b50a6a8a0..e4a65187f 100644
|
||||
--- a/cloudinit/net/sysconfig.py
|
||||
+++ b/cloudinit/net/sysconfig.py
|
||||
@@ -6,7 +6,7 @@ import io
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
-from typing import Mapping, Optional
|
||||
+from typing import Dict, Optional
|
||||
|
||||
from cloudinit import subp, util
|
||||
from cloudinit.distros.parsers import networkmanager_conf, resolv_conf
|
||||
@@ -721,7 +721,7 @@ class Renderer(renderer.Renderer):
|
||||
):
|
||||
physical_filter = renderer.filter_by_physical
|
||||
for iface in network_state.iter_interfaces(physical_filter):
|
||||
- iface_name = iface["name"]
|
||||
+ iface_name = iface.get("config_id") or iface["name"]
|
||||
iface_subnets = iface.get("subnets", [])
|
||||
iface_cfg = iface_contents[iface_name]
|
||||
route_cfg = iface_cfg.routes
|
||||
@@ -924,7 +924,9 @@ class Renderer(renderer.Renderer):
|
||||
return out
|
||||
|
||||
@classmethod
|
||||
- def _render_bridge_interfaces(cls, network_state, iface_contents, flavor):
|
||||
+ def _render_bridge_interfaces(
|
||||
+ cls, network_state: NetworkState, iface_contents, flavor
|
||||
+ ):
|
||||
bridge_key_map = {
|
||||
old_k: new_k
|
||||
for old_k, new_k in cls.cfg_key_maps[flavor].items()
|
||||
@@ -1005,23 +1007,29 @@ class Renderer(renderer.Renderer):
|
||||
|
||||
@classmethod
|
||||
def _render_sysconfig(
|
||||
- cls, base_sysconf_dir, network_state, flavor, templates=None
|
||||
+ cls,
|
||||
+ base_sysconf_dir,
|
||||
+ network_state: NetworkState,
|
||||
+ flavor,
|
||||
+ templates=None,
|
||||
):
|
||||
"""Given state, return /etc/sysconfig files + contents"""
|
||||
if not templates:
|
||||
templates = cls.templates
|
||||
- iface_contents: Mapping[str, NetInterface] = {}
|
||||
+ iface_contents: Dict[str, NetInterface] = {}
|
||||
for iface in network_state.iter_interfaces():
|
||||
if iface["type"] == "loopback":
|
||||
continue
|
||||
- iface_name = iface["name"]
|
||||
- iface_cfg = NetInterface(iface_name, base_sysconf_dir, templates)
|
||||
+ config_id: str = iface.get("config_id") or iface["name"]
|
||||
+ iface_cfg = NetInterface(
|
||||
+ iface["name"], base_sysconf_dir, templates
|
||||
+ )
|
||||
if flavor == "suse":
|
||||
iface_cfg.drop("DEVICE")
|
||||
# If type detection fails it is considered a bug in SUSE
|
||||
iface_cfg.drop("TYPE")
|
||||
cls._render_iface_shared(iface, iface_cfg, flavor)
|
||||
- iface_contents[iface_name] = iface_cfg
|
||||
+ iface_contents[config_id] = iface_cfg
|
||||
cls._render_physical_interfaces(network_state, iface_contents, flavor)
|
||||
cls._render_bond_interfaces(network_state, iface_contents, flavor)
|
||||
cls._render_vlan_interfaces(network_state, iface_contents, flavor)
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index 4673e4eaf..004da81ab 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -4489,6 +4489,95 @@ iface bond0 inet6 static
|
||||
"""
|
||||
),
|
||||
},
|
||||
+ "v2-bridges-set-name": {
|
||||
+ "yaml": textwrap.dedent(
|
||||
+ """\
|
||||
+ version: 2
|
||||
+ ethernets:
|
||||
+ baremetalport:
|
||||
+ match:
|
||||
+ macaddress: 52:54:00:bd:8f:cb
|
||||
+ set-name: baremetal0
|
||||
+ provisioningport:
|
||||
+ match:
|
||||
+ macaddress: 52:54:00:25:ae:12
|
||||
+ set-name: provisioning0
|
||||
+ bridges:
|
||||
+ baremetal:
|
||||
+ addresses:
|
||||
+ - fc00:1:1::2/64
|
||||
+ interfaces:
|
||||
+ - baremetalport
|
||||
+ provisioning:
|
||||
+ addresses:
|
||||
+ - fc00:1:2::2/64
|
||||
+ interfaces:
|
||||
+ - provisioningport
|
||||
+ """
|
||||
+ ),
|
||||
+ "expected_sysconfig_rhel": {
|
||||
+ "ifcfg-baremetal": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Created by cloud-init automatically, do not edit.
|
||||
+ #
|
||||
+ AUTOCONNECT_PRIORITY=120
|
||||
+ BOOTPROTO=none
|
||||
+ DEVICE=baremetal
|
||||
+ IPV6ADDR=fc00:1:1::2/64
|
||||
+ IPV6INIT=yes
|
||||
+ IPV6_AUTOCONF=no
|
||||
+ IPV6_FORCE_ACCEPT_RA=no
|
||||
+ ONBOOT=yes
|
||||
+ TYPE=Bridge
|
||||
+ USERCTL=no
|
||||
+ """
|
||||
+ ),
|
||||
+ "ifcfg-baremetal0": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Created by cloud-init automatically, do not edit.
|
||||
+ #
|
||||
+ AUTOCONNECT_PRIORITY=120
|
||||
+ BOOTPROTO=none
|
||||
+ BRIDGE=baremetal
|
||||
+ DEVICE=baremetal0
|
||||
+ HWADDR=52:54:00:bd:8f:cb
|
||||
+ ONBOOT=yes
|
||||
+ TYPE=Ethernet
|
||||
+ USERCTL=no
|
||||
+ """
|
||||
+ ),
|
||||
+ "ifcfg-provisioning": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Created by cloud-init automatically, do not edit.
|
||||
+ #
|
||||
+ AUTOCONNECT_PRIORITY=120
|
||||
+ BOOTPROTO=none
|
||||
+ DEVICE=provisioning
|
||||
+ IPV6ADDR=fc00:1:2::2/64
|
||||
+ IPV6INIT=yes
|
||||
+ IPV6_AUTOCONF=no
|
||||
+ IPV6_FORCE_ACCEPT_RA=no
|
||||
+ ONBOOT=yes
|
||||
+ TYPE=Bridge
|
||||
+ USERCTL=no
|
||||
+ """
|
||||
+ ),
|
||||
+ "ifcfg-provisioning0": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Created by cloud-init automatically, do not edit.
|
||||
+ #
|
||||
+ AUTOCONNECT_PRIORITY=120
|
||||
+ BOOTPROTO=none
|
||||
+ BRIDGE=provisioning
|
||||
+ DEVICE=provisioning0
|
||||
+ HWADDR=52:54:00:25:ae:12
|
||||
+ ONBOOT=yes
|
||||
+ TYPE=Ethernet
|
||||
+ USERCTL=no
|
||||
+ """
|
||||
+ ),
|
||||
+ },
|
||||
+ },
|
||||
}
|
||||
|
||||
|
||||
@@ -5558,6 +5647,13 @@ USERCTL=no
|
||||
self._compare_files_to_expected(entry[self.expected_name], found)
|
||||
self._assert_headers(found)
|
||||
|
||||
+ def test_bridges_set_name_config(self):
|
||||
+ entry = NETWORK_CONFIGS["v2-bridges-set-name"]
|
||||
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
||||
+ self._compare_files_to_expected(
|
||||
+ entry[self.expected_name], found)
|
||||
+ self._assert_headers(found)
|
||||
+
|
||||
def test_netplan_dhcp_false_disable_dhcp_in_state(self):
|
||||
"""netplan config with dhcp[46]: False should not add dhcp in state"""
|
||||
net_config = yaml.load(NETPLAN_DHCP_FALSE)
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,160 @@
|
||||
From 58904df7d689df6e3d1d4ddf47b5cb5a267006da Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Fri, 11 Oct 2024 13:58:19 -0500
|
||||
Subject: [PATCH 2/2] fix: Render v2 bridges correctly on network-manager with
|
||||
set-name (#5740)
|
||||
|
||||
RH-Author: xiachen <xiachen@redhat.com>
|
||||
RH-MergeRequest: 147: fix: Render bridges correctly for v2 on sysconfig with set-name (#5674)
|
||||
RH-Jira: RHEL-65021
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
|
||||
RH-Commit: [2/2] e101d8ff06a379752958a3eb19d209bcb7d1e0ed (xiachen/cloud-init)
|
||||
|
||||
Similar to the recent sysconfig fix, ensure bridges render correctly
|
||||
for configs that contain `set-name`.
|
||||
|
||||
Fixes GH-5717
|
||||
|
||||
(cherry picked from commit 9554338e6ecf49c66324cc637eaf0fa7bf10e407)
|
||||
Signed-off-by: Amy Chen <xiachen@redhat.com>
|
||||
---
|
||||
cloudinit/net/network_manager.py | 6 +-
|
||||
tests/unittests/test_net.py | 94 ++++++++++++++++++++++++++++++++
|
||||
2 files changed, 98 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
|
||||
index 0ba210b74..f50fafa39 100644
|
||||
--- a/cloudinit/net/network_manager.py
|
||||
+++ b/cloudinit/net/network_manager.py
|
||||
@@ -464,11 +464,13 @@ class Renderer(renderer.Renderer):
|
||||
# interfaces that have UUIDs that can be linked to from related
|
||||
# interfaces
|
||||
for iface in network_state.iter_interfaces():
|
||||
- self.connections[iface["name"]] = NMConnection(iface["name"])
|
||||
+ conn_key = iface.get("config_id") or iface["name"]
|
||||
+ self.connections[conn_key] = NMConnection(iface["name"])
|
||||
|
||||
# Now render the actual interface configuration
|
||||
for iface in network_state.iter_interfaces():
|
||||
- conn = self.connections[iface["name"]]
|
||||
+ conn_key = iface.get("config_id") or iface["name"]
|
||||
+ conn = self.connections[conn_key]
|
||||
conn.render_interface(iface, network_state, self)
|
||||
|
||||
# And finally write the files
|
||||
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
||||
index 004da81ab..ddb45dc69 100644
|
||||
--- a/tests/unittests/test_net.py
|
||||
+++ b/tests/unittests/test_net.py
|
||||
@@ -4577,6 +4577,94 @@ iface bond0 inet6 static
|
||||
"""
|
||||
),
|
||||
},
|
||||
+ "expected_network_manager": {
|
||||
+ "cloud-init-baremetal.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init baremetal
|
||||
+ uuid=e63eed9a-cd1c-55de-8d5e-1e80b756a482
|
||||
+ autoconnect-priority=120
|
||||
+ type=bridge
|
||||
+ interface-name=baremetal
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [bridge]
|
||||
+
|
||||
+ [ipv6]
|
||||
+ method=manual
|
||||
+ may-fail=false
|
||||
+ address1=fc00:1:1::2/64
|
||||
+
|
||||
+ """
|
||||
+ ),
|
||||
+ "cloud-init-baremetalport.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init baremetal0
|
||||
+ uuid=8e326690-51d6-5157-ab84-e4e822b06503
|
||||
+ autoconnect-priority=120
|
||||
+ type=ethernet
|
||||
+ slave-type=bridge
|
||||
+ master=e63eed9a-cd1c-55de-8d5e-1e80b756a482
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [ethernet]
|
||||
+ mac-address=52:54:00:BD:8F:CB
|
||||
+
|
||||
+ """
|
||||
+ ),
|
||||
+ "cloud-init-provisioning.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init provisioning
|
||||
+ uuid=e5bd3f1a-cdcc-55d3-a6d8-88f1ba73bd0e
|
||||
+ autoconnect-priority=120
|
||||
+ type=bridge
|
||||
+ interface-name=provisioning
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [bridge]
|
||||
+
|
||||
+ [ipv6]
|
||||
+ method=manual
|
||||
+ may-fail=false
|
||||
+ address1=fc00:1:2::2/64
|
||||
+
|
||||
+ """
|
||||
+ ),
|
||||
+ "cloud-init-provisioningport.nmconnection": textwrap.dedent(
|
||||
+ """\
|
||||
+ # Generated by cloud-init. Changes will be lost.
|
||||
+
|
||||
+ [connection]
|
||||
+ id=cloud-init provisioning0
|
||||
+ uuid=d79b7b70-e9df-596f-ace7-89537db45684
|
||||
+ autoconnect-priority=120
|
||||
+ type=ethernet
|
||||
+ slave-type=bridge
|
||||
+ master=e5bd3f1a-cdcc-55d3-a6d8-88f1ba73bd0e
|
||||
+
|
||||
+ [user]
|
||||
+ org.freedesktop.NetworkManager.origin=cloud-init
|
||||
+
|
||||
+ [ethernet]
|
||||
+ mac-address=52:54:00:25:AE:12
|
||||
+
|
||||
+ """
|
||||
+ ),
|
||||
+ },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6722,6 +6810,12 @@ class TestNetworkManagerRendering(CiTestCase):
|
||||
entry[self.expected_name], self.expected_conf_d, found
|
||||
)
|
||||
|
||||
+ def test_v2_bridges_set_name(self):
|
||||
+ entry = NETWORK_CONFIGS["v2-bridges-set-name"]
|
||||
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
||||
+ self._compare_files_to_expected(
|
||||
+ entry[self.expected_name], self.expected_conf_d, found
|
||||
+ )
|
||||
|
||||
@mock.patch(
|
||||
"cloudinit.net.is_openvswitch_internal_interface",
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,42 @@
|
||||
From 332bb23bcfde801edf792e6c629ec350be07b952 Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Tue, 19 Mar 2024 14:24:11 -0500
|
||||
Subject: [PATCH 3/3] fix: Undeprecate 'network' in schema route definition
|
||||
(#5072)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 73: fix: Undeprecate 'network' in schema route definition (#5072)
|
||||
RH-Jira: RHEL-29709
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [1/1] 61c660be43fd25999bca0cfd66d7b2150fee5a14 (anisinha/cloud-init)
|
||||
|
||||
It is passed through to our v1 schema from OpenStack network_data.json
|
||||
|
||||
Fixes GH-5051
|
||||
|
||||
(cherry picked from commit ff40d1af8a6de3ee27937382ec4ceea931d80a88)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/config/schemas/schema-network-config-v1.json | 5 +----
|
||||
1 file changed, 1 insertion(+), 4 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/config/schemas/schema-network-config-v1.json b/cloudinit/config/schemas/schema-network-config-v1.json
|
||||
index 56dc27c9..64c492a4 100644
|
||||
--- a/cloudinit/config/schemas/schema-network-config-v1.json
|
||||
+++ b/cloudinit/config/schemas/schema-network-config-v1.json
|
||||
@@ -445,10 +445,7 @@
|
||||
},
|
||||
"network": {
|
||||
"type": "string",
|
||||
- "description": "IPv4 network address with CIDR netmask notation or IPv6 with prefix length. Alias for ``destination`` and only read when ``destination`` key is absent.",
|
||||
- "deprecated": true,
|
||||
- "deprecated_version": "23.3",
|
||||
- "deprecated_description": "Use ``destination`` instead."
|
||||
+ "description": "IPv4 network address with CIDR netmask notation or IPv6 with prefix length. Alias for ``destination`` and only read when ``destination`` key is absent. This exists for OpenStack support. OpenStack route definitions are passed through to v1 config and OpenStack's ``network_data.json`` uses ``network`` instead of ``destination``."
|
||||
},
|
||||
"destination": {
|
||||
"type": "string",
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,58 @@
|
||||
From fcaff2e02a07af587d8366f61df1685435e32288 Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Wed, 3 Jan 2024 09:11:40 -0700
|
||||
Subject: [PATCH] fix(cloudstack): Use parsed lease file for virtual router in
|
||||
cloudstack
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 85: fix(cloudstack): Use parsed lease file for virtual router in cloudstack
|
||||
RH-Jira: RHEL-40217
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/1] 9d1353620ee2c773170e424479bb2664116554f4 (anisinha/cloud-init)
|
||||
|
||||
Fixes 5942f4023e2581a
|
||||
|
||||
(cherry picked from commit cb36bf38b823f811a3e938ccffc03d7d13190095)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/sources/DataSourceCloudStack.py | 22 +++++++++++-----------
|
||||
1 file changed, 11 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py
|
||||
index fd2482a3..f752765d 100644
|
||||
--- a/cloudinit/sources/DataSourceCloudStack.py
|
||||
+++ b/cloudinit/sources/DataSourceCloudStack.py
|
||||
@@ -229,18 +229,18 @@ def get_vr_address():
|
||||
)
|
||||
return latest_address
|
||||
|
||||
- # Try dhcp lease files next...
|
||||
+ # Try dhcp lease files next
|
||||
lease_file = dhcp.IscDhclient.get_latest_lease()
|
||||
- if not lease_file:
|
||||
- LOG.debug("No lease file found, using default gateway")
|
||||
- return get_default_gateway()
|
||||
-
|
||||
- lease_file = dhcp.IscDhclient.parse_dhcp_server_from_lease_file(lease_file)
|
||||
- if not latest_address:
|
||||
- # No virtual router found, fallback on default gateway
|
||||
- LOG.debug("No DHCP found, using default gateway")
|
||||
- return get_default_gateway()
|
||||
- return latest_address
|
||||
+ if lease_file:
|
||||
+ latest_address = dhcp.IscDhclient.parse_dhcp_server_from_lease_file(
|
||||
+ lease_file
|
||||
+ )
|
||||
+ if latest_address:
|
||||
+ return latest_address
|
||||
+
|
||||
+ # No virtual router found, fallback to default gateway
|
||||
+ LOG.debug("No DHCP found, using default gateway")
|
||||
+ return get_default_gateway()
|
||||
|
||||
|
||||
# Used to match classes to dependencies
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,132 @@
|
||||
From 2b74b0eb94edfd7caa42bc0d8affc37311ba041b Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Wed, 3 Jan 2024 09:11:21 -0700
|
||||
Subject: [PATCH 4/6] fix(dhcp): Guard against FileNotFoundError and NameError
|
||||
exceptions
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 92: Update pylint version to support python 3.12
|
||||
RH-Jira: RHEL-44598
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||||
RH-Commit: [1/3] 730b8de9ceb2c380d3b15573d83691ab95a1487e (anisinha/cloud-init)
|
||||
|
||||
(cherry picked from commit 53eb8555e091474803b724700815adc09aa84f05)
|
||||
---
|
||||
cloudinit/net/dhcp.py | 20 ++++++++++------
|
||||
tests/unittests/net/test_dhcp.py | 40 ++++++++++++++++++++++++++++++++
|
||||
2 files changed, 53 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
|
||||
index 07c13390..a0aee98c 100644
|
||||
--- a/cloudinit/net/dhcp.py
|
||||
+++ b/cloudinit/net/dhcp.py
|
||||
@@ -5,15 +5,15 @@
|
||||
# This file is part of cloud-init. See LICENSE file for license information.
|
||||
|
||||
import abc
|
||||
-import contextlib
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import time
|
||||
+from contextlib import suppress
|
||||
from io import StringIO
|
||||
-from typing import Any, Dict, List
|
||||
+from typing import Any, Dict, List, Optional
|
||||
|
||||
import configobj
|
||||
|
||||
@@ -268,7 +268,7 @@ class IscDhclient(DhcpClient):
|
||||
|
||||
# this function waits for these files to exist, clean previous runs
|
||||
# to avoid false positive in wait_for_files
|
||||
- with contextlib.suppress(FileNotFoundError):
|
||||
+ with suppress(FileNotFoundError):
|
||||
os.remove(pid_file)
|
||||
os.remove(lease_file)
|
||||
|
||||
@@ -514,9 +514,15 @@ class IscDhclient(DhcpClient):
|
||||
return latest_file
|
||||
|
||||
@staticmethod
|
||||
- def parse_dhcp_server_from_lease_file(lease_file):
|
||||
- with open(lease_file, "r") as fd:
|
||||
- for line in fd:
|
||||
+ def parse_dhcp_server_from_lease_file(lease_file) -> Optional[str]:
|
||||
+ """Parse a lease file for the dhcp server address
|
||||
+
|
||||
+ @param lease_file: Name of a file to be parsed
|
||||
+ @return: An address if found, or None
|
||||
+ """
|
||||
+ latest_address = None
|
||||
+ with suppress(FileNotFoundError), open(lease_file, "r") as file:
|
||||
+ for line in file:
|
||||
if "dhcp-server-identifier" in line:
|
||||
words = line.strip(" ;\r\n").split(" ")
|
||||
if len(words) > 2:
|
||||
@@ -561,7 +567,7 @@ class Udhcpc(DhcpClient):
|
||||
|
||||
tmp_dir = temp_utils.get_tmp_ancestor(needs_exe=True)
|
||||
lease_file = os.path.join(tmp_dir, interface + ".lease.json")
|
||||
- with contextlib.suppress(FileNotFoundError):
|
||||
+ with suppress(FileNotFoundError):
|
||||
os.remove(lease_file)
|
||||
|
||||
# udhcpc needs the interface up to send initial discovery packets
|
||||
diff --git a/tests/unittests/net/test_dhcp.py b/tests/unittests/net/test_dhcp.py
|
||||
index a7b62312..8ec96eef 100644
|
||||
--- a/tests/unittests/net/test_dhcp.py
|
||||
+++ b/tests/unittests/net/test_dhcp.py
|
||||
@@ -32,6 +32,46 @@ LEASE_F = "/run/dhclient.lease"
|
||||
DHCLIENT = "/sbin/dhclient"
|
||||
|
||||
|
||||
+@pytest.mark.parametrize(
|
||||
+ "server_address,lease_file_content",
|
||||
+ (
|
||||
+ pytest.param(None, None, id="no_server_addr_on_absent_lease_file"),
|
||||
+ pytest.param(None, "", id="no_server_addr_on_empty_lease_file"),
|
||||
+ pytest.param(
|
||||
+ None,
|
||||
+ "lease {\n fixed-address: 10.1.2.3;\n}\n",
|
||||
+ id="no_server_addr_when_no_server_ident",
|
||||
+ ),
|
||||
+ pytest.param(
|
||||
+ "10.4.5.6",
|
||||
+ "lease {\n fixed-address: 10.1.2.3;\n"
|
||||
+ " option dhcp-server-identifier 10.4.5.6;\n"
|
||||
+ " option dhcp-renewal-time 1800;\n}\n",
|
||||
+ id="server_addr_found_when_server_ident_present",
|
||||
+ ),
|
||||
+ ),
|
||||
+)
|
||||
+class TestParseDHCPServerFromLeaseFile:
|
||||
+ def test_find_server_address_when_present(
|
||||
+ self, server_address, lease_file_content, tmp_path
|
||||
+ ):
|
||||
+ """Test that we return None in the case of no file or file contains no
|
||||
+ server address, otherwise return the address.
|
||||
+ """
|
||||
+ lease_file = tmp_path / "dhcp.leases"
|
||||
+ if server_address:
|
||||
+ if lease_file_content:
|
||||
+ lease_file.write_text(lease_file_content)
|
||||
+ assert (
|
||||
+ server_address
|
||||
+ == IscDhclient.parse_dhcp_server_from_lease_file(lease_file)
|
||||
+ )
|
||||
+ else:
|
||||
+ assert not IscDhclient.parse_dhcp_server_from_lease_file(
|
||||
+ lease_file
|
||||
+ )
|
||||
+
|
||||
+
|
||||
class TestParseDHCPLeasesFile(CiTestCase):
|
||||
def test_parse_empty_lease_file_errors(self):
|
||||
"""parse_dhcp_lease_file errors when file content is empty."""
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,34 @@
|
||||
From e6e7c274235d924fde752b228f68ec5773e39029 Mon Sep 17 00:00:00 2001
|
||||
From: Brett Holman <brett.holman@canonical.com>
|
||||
Date: Tue, 5 Dec 2023 16:40:03 -0700
|
||||
Subject: [PATCH 2/2] fix(python3.13): Fix import error for passlib on Python
|
||||
3.13 (#4669)
|
||||
|
||||
RH-Author: xiachen <xiachen@redhat.com>
|
||||
RH-MergeRequest: 145: Fix metric setting for ifcfg network connections for rhel (#5777)
|
||||
RH-Jira: RHEL-65018
|
||||
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
|
||||
RH-Commit: [2/2] 226ae26a9f903774cb36f8f72b89071ba4545a66 (xiachen/cloud-init)
|
||||
|
||||
(cherry picked from commit 09b70436b3a0aae1fe24fdde6e8cdd7ee98d9c15)
|
||||
Signed-off-by: Amy Chen <xiachen@redhat.com>
|
||||
---
|
||||
cloudinit/sources/DataSourceAzure.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
|
||||
index eb0304c3d..939210100 100644
|
||||
--- a/cloudinit/sources/DataSourceAzure.py
|
||||
+++ b/cloudinit/sources/DataSourceAzure.py
|
||||
@@ -58,7 +58,7 @@ try:
|
||||
)
|
||||
except (ImportError, AttributeError):
|
||||
try:
|
||||
- import passlib
|
||||
+ import passlib.hash
|
||||
|
||||
blowfish_hash = passlib.hash.sha512_crypt.hash
|
||||
except ImportError:
|
||||
--
|
||||
2.39.3
|
||||
|
@ -0,0 +1,91 @@
|
||||
From 9a3c16dc89d2813c90df2e57e71ae5df704083be Mon Sep 17 00:00:00 2001
|
||||
From: James Falcon <james.falcon@canonical.com>
|
||||
Date: Tue, 9 Jan 2024 10:32:12 -0600
|
||||
Subject: [PATCH] refactor: Ensure internal DNS state same for v1 and v2
|
||||
(#4756)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 152: refactor: Ensure internal DNS state same for v1 and v2 (#4756)
|
||||
RH-Jira: RHEL-68409
|
||||
RH-Acked-by: xiachen <xiachen@redhat.com>
|
||||
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
RH-Commit: [1/1] f006996f7b418103ffaf73ff9ded5b5d149bedf6
|
||||
|
||||
When defining interface-level DNS on the network state, v1 uses the
|
||||
"nameservers" key whereas v2 was using an "addresses" key.
|
||||
This commit updates the v2 parsing to set the key as "nameservers".
|
||||
|
||||
Also update networkd renderer as this was only renderer using the v2
|
||||
"addresses" key.
|
||||
|
||||
(cherry picked from commit 436e6f5ce3fbb8b391a2158538873644058904e6)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/net/network_state.py | 2 +-
|
||||
cloudinit/net/networkd.py | 13 ++++---------
|
||||
tests/unittests/net/test_network_state.py | 5 ++++-
|
||||
3 files changed, 9 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
|
||||
index 226421bd0..82c3bf894 100644
|
||||
--- a/cloudinit/net/network_state.py
|
||||
+++ b/cloudinit/net/network_state.py
|
||||
@@ -336,7 +336,7 @@ class NetworkStateInterpreter:
|
||||
if iface:
|
||||
nameservers, search = dns
|
||||
iface["dns"] = {
|
||||
- "addresses": nameservers,
|
||||
+ "nameservers": nameservers,
|
||||
"search": search,
|
||||
}
|
||||
|
||||
diff --git a/cloudinit/net/networkd.py b/cloudinit/net/networkd.py
|
||||
index 0978849c8..29f466eda 100644
|
||||
--- a/cloudinit/net/networkd.py
|
||||
+++ b/cloudinit/net/networkd.py
|
||||
@@ -221,12 +221,6 @@ class Renderer(renderer.Renderer):
|
||||
def parse_dns(self, iface, cfg: CfgParser, ns: NetworkState):
|
||||
sec = "Network"
|
||||
|
||||
- dns_cfg_map = {
|
||||
- "search": "Domains",
|
||||
- "nameservers": "DNS",
|
||||
- "addresses": "DNS",
|
||||
- }
|
||||
-
|
||||
dns = iface.get("dns")
|
||||
if not dns and ns.version == 1:
|
||||
dns = {
|
||||
@@ -236,9 +230,10 @@ class Renderer(renderer.Renderer):
|
||||
elif not dns and ns.version == 2:
|
||||
return
|
||||
|
||||
- for k, v in dns_cfg_map.items():
|
||||
- if k in dns and dns[k]:
|
||||
- cfg.update_section(sec, v, " ".join(dns[k]))
|
||||
+ if dns.get("search"):
|
||||
+ cfg.update_section(sec, "Domains", " ".join(dns["search"]))
|
||||
+ if dns.get("nameservers"):
|
||||
+ cfg.update_section(sec, "DNS", " ".join(dns["nameservers"]))
|
||||
|
||||
def parse_dhcp_overrides(self, cfg: CfgParser, device, dhcp, version):
|
||||
dhcp_config_maps = {
|
||||
diff --git a/tests/unittests/net/test_network_state.py b/tests/unittests/net/test_network_state.py
|
||||
index 7d304ca3a..74a6bb34c 100644
|
||||
--- a/tests/unittests/net/test_network_state.py
|
||||
+++ b/tests/unittests/net/test_network_state.py
|
||||
@@ -258,7 +258,10 @@ class TestNetworkStateParseNameservers:
|
||||
# If an interface was specified, DNS should be part of the interface
|
||||
for iface in config.iter_interfaces():
|
||||
if iface["name"] == "eth1":
|
||||
- assert iface["dns"]["addresses"] == ["192.168.1.1", "8.8.8.8"]
|
||||
+ assert iface["dns"]["nameservers"] == [
|
||||
+ "192.168.1.1",
|
||||
+ "8.8.8.8",
|
||||
+ ]
|
||||
assert iface["dns"]["search"] == ["spam.local"]
|
||||
else:
|
||||
assert "dns" not in iface
|
||||
--
|
||||
2.39.3
|
||||
|
@ -1,544 +0,0 @@
|
||||
From d5c2095abb4d22fc976ed3011679134c75bead99 Mon Sep 17 00:00:00 2001
|
||||
From: Cat Red <catmsred@users.noreply.github.com>
|
||||
Date: Mon, 4 Mar 2024 21:38:14 -0500
|
||||
Subject: [PATCH 2/3] refactor: remove dependency on netifaces (#4634)
|
||||
|
||||
RH-Author: Ani Sinha <anisinha@redhat.com>
|
||||
RH-MergeRequest: 80: refactor: remove dependency on netifaces (#4634)
|
||||
RH-Jira: RHEL-34518
|
||||
RH-Acked-by: xiachen <xiachen@redhat.com>
|
||||
RH-Acked-by: Cathy Avery <cavery@redhat.com>
|
||||
RH-Commit: [1/2] e55e7a588301234f62dfaf36080fb5f95aa52b2f (anisinha/cloud-init)
|
||||
|
||||
Upstream netifaces is no longer being maintained and is only
|
||||
used by the VMWare data source. As such this commit
|
||||
replaces the calls to netifaces with cloudinit's native netinfo.
|
||||
|
||||
(cherry picked from commit 2ba7fdf0e1eb0bc597ceac8903695f67571fd873)
|
||||
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
||||
---
|
||||
cloudinit/sources/DataSourceVMware.py | 207 ++++++++++---------------
|
||||
pyproject.toml | 1 -
|
||||
requirements.txt | 9 --
|
||||
tests/unittests/sources/test_vmware.py | 161 ++++++++++++++++++-
|
||||
tools/build-on-netbsd | 1 -
|
||||
tools/build-on-openbsd | 1 -
|
||||
tox.ini | 1 -
|
||||
7 files changed, 239 insertions(+), 142 deletions(-)
|
||||
|
||||
diff --git a/cloudinit/sources/DataSourceVMware.py b/cloudinit/sources/DataSourceVMware.py
|
||||
index 2a91a307..6ed6a6a5 100644
|
||||
--- a/cloudinit/sources/DataSourceVMware.py
|
||||
+++ b/cloudinit/sources/DataSourceVMware.py
|
||||
@@ -16,51 +16,6 @@ multiple transports types, including:
|
||||
* EnvVars
|
||||
* GuestInfo
|
||||
* IMC (Guest Customization)
|
||||
-
|
||||
-Netifaces (https://github.com/al45tair/netifaces)
|
||||
-
|
||||
- Please note this module relies on the netifaces project to introspect the
|
||||
- runtime, network configuration of the host on which this datasource is
|
||||
- running. This is in contrast to the rest of cloud-init which uses the
|
||||
- cloudinit/netinfo module.
|
||||
-
|
||||
- The reasons for using netifaces include:
|
||||
-
|
||||
- * Netifaces is built in C and is more portable across multiple systems
|
||||
- and more deterministic than shell exec'ing local network commands and
|
||||
- parsing their output.
|
||||
-
|
||||
- * Netifaces provides a stable way to determine the view of the host's
|
||||
- network after DHCP has brought the network online. Unlike most other
|
||||
- datasources, this datasource still provides support for JINJA queries
|
||||
- based on networking information even when the network is based on a
|
||||
- DHCP lease. While this does not tie this datasource directly to
|
||||
- netifaces, it does mean the ability to consistently obtain the
|
||||
- correct information is paramount.
|
||||
-
|
||||
- * It is currently possible to execute this datasource on macOS
|
||||
- (which many developers use today) to print the output of the
|
||||
- get_host_info function. This function calls netifaces to obtain
|
||||
- the same runtime network configuration that the datasource would
|
||||
- persist to the local system's instance data.
|
||||
-
|
||||
- However, the netinfo module fails on macOS. The result is either a
|
||||
- hung operation that requires a SIGINT to return control to the user,
|
||||
- or, if brew is used to install iproute2mac, the ip commands are used
|
||||
- but produce output the netinfo module is unable to parse.
|
||||
-
|
||||
- While macOS is not a target of cloud-init, this feature is quite
|
||||
- useful when working on this datasource.
|
||||
-
|
||||
- For more information about this behavior, please see the following
|
||||
- PR comment, https://bit.ly/3fG7OVh.
|
||||
-
|
||||
- The authors of this datasource are not opposed to moving away from
|
||||
- netifaces. The goal may be to eventually do just that. This proviso was
|
||||
- added to the top of this module as a way to remind future-us and others
|
||||
- why netifaces was used in the first place in order to either smooth the
|
||||
- transition away from netifaces or embrace it further up the cloud-init
|
||||
- stack.
|
||||
"""
|
||||
|
||||
import collections
|
||||
@@ -72,9 +27,7 @@ import os
|
||||
import socket
|
||||
import time
|
||||
|
||||
-import netifaces
|
||||
-
|
||||
-from cloudinit import atomic_helper, dmi, log, net, sources, util
|
||||
+from cloudinit import atomic_helper, dmi, log, net, netinfo, sources, util
|
||||
from cloudinit.sources.helpers.vmware.imc import guestcust_util
|
||||
from cloudinit.subp import ProcessExecutionError, subp, which
|
||||
|
||||
@@ -814,91 +767,64 @@ def get_default_ip_addrs():
|
||||
addresses associated with the device used by the default route for a given
|
||||
address.
|
||||
"""
|
||||
- # TODO(promote and use netifaces in cloudinit.net* modules)
|
||||
- gateways = netifaces.gateways()
|
||||
- if "default" not in gateways:
|
||||
- return None, None
|
||||
-
|
||||
- default_gw = gateways["default"]
|
||||
- if (
|
||||
- netifaces.AF_INET not in default_gw
|
||||
- and netifaces.AF_INET6 not in default_gw
|
||||
- ):
|
||||
- return None, None
|
||||
|
||||
+ # Get ipv4 and ipv6 interfaces associated with default routes
|
||||
+ ipv4_if = None
|
||||
+ ipv6_if = None
|
||||
+ routes = netinfo.route_info()
|
||||
+ for route in routes["ipv4"]:
|
||||
+ if route["destination"] == "0.0.0.0":
|
||||
+ ipv4_if = route["iface"]
|
||||
+ break
|
||||
+ for route in routes["ipv6"]:
|
||||
+ if route["destination"] == "::/0":
|
||||
+ ipv6_if = route["iface"]
|
||||
+ break
|
||||
+
|
||||
+ # Get ip address associated with default interface
|
||||
ipv4 = None
|
||||
ipv6 = None
|
||||
-
|
||||
- gw4 = default_gw.get(netifaces.AF_INET)
|
||||
- if gw4:
|
||||
- _, dev4 = gw4
|
||||
- addr4_fams = netifaces.ifaddresses(dev4)
|
||||
- if addr4_fams:
|
||||
- af_inet4 = addr4_fams.get(netifaces.AF_INET)
|
||||
- if af_inet4:
|
||||
- if len(af_inet4) > 1:
|
||||
- LOG.debug(
|
||||
- "device %s has more than one ipv4 address: %s",
|
||||
- dev4,
|
||||
- af_inet4,
|
||||
- )
|
||||
- elif "addr" in af_inet4[0]:
|
||||
- ipv4 = af_inet4[0]["addr"]
|
||||
-
|
||||
- # Try to get the default IPv6 address by first seeing if there is a default
|
||||
- # IPv6 route.
|
||||
- gw6 = default_gw.get(netifaces.AF_INET6)
|
||||
- if gw6:
|
||||
- _, dev6 = gw6
|
||||
- addr6_fams = netifaces.ifaddresses(dev6)
|
||||
- if addr6_fams:
|
||||
- af_inet6 = addr6_fams.get(netifaces.AF_INET6)
|
||||
- if af_inet6:
|
||||
- if len(af_inet6) > 1:
|
||||
- LOG.debug(
|
||||
- "device %s has more than one ipv6 address: %s",
|
||||
- dev6,
|
||||
- af_inet6,
|
||||
- )
|
||||
- elif "addr" in af_inet6[0]:
|
||||
- ipv6 = af_inet6[0]["addr"]
|
||||
+ netdev = netinfo.netdev_info()
|
||||
+ if ipv4_if in netdev:
|
||||
+ addrs = netdev[ipv4_if]["ipv4"]
|
||||
+ if len(addrs) > 1:
|
||||
+ LOG.debug(
|
||||
+ "device %s has more than one ipv4 address: %s", ipv4_if, addrs
|
||||
+ )
|
||||
+ elif len(addrs) == 1 and "ip" in addrs[0]:
|
||||
+ ipv4 = addrs[0]["ip"]
|
||||
+ if ipv6_if in netdev:
|
||||
+ addrs = netdev[ipv6_if]["ipv6"]
|
||||
+ if len(addrs) > 1:
|
||||
+ LOG.debug(
|
||||
+ "device %s has more than one ipv6 address: %s", ipv6_if, addrs
|
||||
+ )
|
||||
+ elif len(addrs) == 1 and "ip" in addrs[0]:
|
||||
+ ipv6 = addrs[0]["ip"]
|
||||
|
||||
# If there is a default IPv4 address but not IPv6, then see if there is a
|
||||
# single IPv6 address associated with the same device associated with the
|
||||
# default IPv4 address.
|
||||
- if ipv4 and not ipv6:
|
||||
- af_inet6 = addr4_fams.get(netifaces.AF_INET6)
|
||||
- if af_inet6:
|
||||
- if len(af_inet6) > 1:
|
||||
- LOG.debug(
|
||||
- "device %s has more than one ipv6 address: %s",
|
||||
- dev4,
|
||||
- af_inet6,
|
||||
- )
|
||||
- elif "addr" in af_inet6[0]:
|
||||
- ipv6 = af_inet6[0]["addr"]
|
||||
+ if ipv4 is not None and ipv6 is None:
|
||||
+ for dev_name in netdev:
|
||||
+ for addr in netdev[dev_name]["ipv4"]:
|
||||
+ if addr["ip"] == ipv4 and len(netdev[dev_name]["ipv6"]) == 1:
|
||||
+ ipv6 = netdev[dev_name]["ipv6"][0]["ip"]
|
||||
+ break
|
||||
|
||||
# If there is a default IPv6 address but not IPv4, then see if there is a
|
||||
# single IPv4 address associated with the same device associated with the
|
||||
# default IPv6 address.
|
||||
- if not ipv4 and ipv6:
|
||||
- af_inet4 = addr6_fams.get(netifaces.AF_INET)
|
||||
- if af_inet4:
|
||||
- if len(af_inet4) > 1:
|
||||
- LOG.debug(
|
||||
- "device %s has more than one ipv4 address: %s",
|
||||
- dev6,
|
||||
- af_inet4,
|
||||
- )
|
||||
- elif "addr" in af_inet4[0]:
|
||||
- ipv4 = af_inet4[0]["addr"]
|
||||
+ if ipv4 is None and ipv6 is not None:
|
||||
+ for dev_name in netdev:
|
||||
+ for addr in netdev[dev_name]["ipv6"]:
|
||||
+ if addr["ip"] == ipv6 and len(netdev[dev_name]["ipv4"]) == 1:
|
||||
+ ipv4 = netdev[dev_name]["ipv4"][0]["ip"]
|
||||
+ break
|
||||
|
||||
return ipv4, ipv6
|
||||
|
||||
|
||||
-# patched socket.getfqdn() - see https://bugs.python.org/issue5004
|
||||
-
|
||||
-
|
||||
def getfqdn(name=""):
|
||||
"""Get fully qualified domain name from name.
|
||||
An empty argument is interpreted as meaning the local host.
|
||||
@@ -933,6 +859,33 @@ def is_valid_ip_addr(val):
|
||||
)
|
||||
|
||||
|
||||
+def convert_to_netifaces_format(addr):
|
||||
+ """
|
||||
+ Takes a cloudinit.netinfo formatted address and converts to netifaces
|
||||
+ format, since this module was originally written with netifaces as the
|
||||
+ network introspection module.
|
||||
+ netifaces format:
|
||||
+ {
|
||||
+ "broadcast": "10.15.255.255",
|
||||
+ "netmask": "255.240.0.0",
|
||||
+ "addr": "10.0.1.4"
|
||||
+ }
|
||||
+
|
||||
+ cloudinit.netinfo format:
|
||||
+ {
|
||||
+ "ip": "10.0.1.4",
|
||||
+ "mask": "255.240.0.0",
|
||||
+ "bcast": "10.15.255.255",
|
||||
+ "scope": "global",
|
||||
+ }
|
||||
+ """
|
||||
+ return {
|
||||
+ "broadcast": addr["bcast"],
|
||||
+ "netmask": addr["mask"],
|
||||
+ "addr": addr["ip"],
|
||||
+ }
|
||||
+
|
||||
+
|
||||
def get_host_info():
|
||||
"""
|
||||
Returns host information such as the host name and network interfaces.
|
||||
@@ -963,16 +916,16 @@ def get_host_info():
|
||||
by_ipv4 = host_info["network"]["interfaces"]["by-ipv4"]
|
||||
by_ipv6 = host_info["network"]["interfaces"]["by-ipv6"]
|
||||
|
||||
- ifaces = netifaces.interfaces()
|
||||
+ ifaces = netinfo.netdev_info()
|
||||
for dev_name in ifaces:
|
||||
- addr_fams = netifaces.ifaddresses(dev_name)
|
||||
- af_link = addr_fams.get(netifaces.AF_LINK)
|
||||
- af_inet4 = addr_fams.get(netifaces.AF_INET)
|
||||
- af_inet6 = addr_fams.get(netifaces.AF_INET6)
|
||||
-
|
||||
- mac = None
|
||||
- if af_link and "addr" in af_link[0]:
|
||||
- mac = af_link[0]["addr"]
|
||||
+ af_inet4 = []
|
||||
+ af_inet6 = []
|
||||
+ for addr in ifaces[dev_name]["ipv4"]:
|
||||
+ af_inet4.append(convert_to_netifaces_format(addr))
|
||||
+ for addr in ifaces[dev_name]["ipv6"]:
|
||||
+ af_inet6.append(convert_to_netifaces_format(addr))
|
||||
+
|
||||
+ mac = ifaces[dev_name].get("hwaddr")
|
||||
|
||||
# Do not bother recording localhost
|
||||
if mac == "00:00:00:00:00:00":
|
||||
diff --git a/pyproject.toml b/pyproject.toml
|
||||
index 99854f39..6f8ccdd1 100644
|
||||
--- a/pyproject.toml
|
||||
+++ b/pyproject.toml
|
||||
@@ -28,7 +28,6 @@ module = [
|
||||
"debconf",
|
||||
"httplib",
|
||||
"jsonpatch",
|
||||
- "netifaces",
|
||||
"paramiko.*",
|
||||
"pip.*",
|
||||
"pycloudlib.*",
|
||||
diff --git a/requirements.txt b/requirements.txt
|
||||
index edec46a7..eabd7a22 100644
|
||||
--- a/requirements.txt
|
||||
+++ b/requirements.txt
|
||||
@@ -29,12 +29,3 @@ jsonpatch
|
||||
|
||||
# For validating cloud-config sections per schema definitions
|
||||
jsonschema
|
||||
-
|
||||
-# Used by DataSourceVMware to inspect the host's network configuration during
|
||||
-# the "setup()" function.
|
||||
-#
|
||||
-# This allows a host that uses DHCP to bring up the network during BootLocal
|
||||
-# and still participate in instance-data by gathering the network in detail at
|
||||
-# runtime and merge that information into the metadata and repersist that to
|
||||
-# disk.
|
||||
-netifaces>=0.10.4
|
||||
diff --git a/tests/unittests/sources/test_vmware.py b/tests/unittests/sources/test_vmware.py
|
||||
index 585f4fbd..33193f89 100644
|
||||
--- a/tests/unittests/sources/test_vmware.py
|
||||
+++ b/tests/unittests/sources/test_vmware.py
|
||||
@@ -63,6 +63,45 @@ runcmd:
|
||||
- echo "Hello, world."
|
||||
"""
|
||||
|
||||
+VMW_IPV4_ROUTEINFO = {
|
||||
+ "destination": "0.0.0.0",
|
||||
+ "flags": "G",
|
||||
+ "gateway": "10.85.130.1",
|
||||
+ "genmask": "0.0.0.0",
|
||||
+ "iface": "eth0",
|
||||
+ "metric": "50",
|
||||
+}
|
||||
+VMW_IPV4_NETDEV_ADDR = {
|
||||
+ "bcast": "10.85.130.255",
|
||||
+ "ip": "10.85.130.116",
|
||||
+ "mask": "255.255.255.0",
|
||||
+ "scope": "global",
|
||||
+}
|
||||
+VMW_IPV6_ROUTEINFO = {
|
||||
+ "destination": "::/0",
|
||||
+ "flags": "UG",
|
||||
+ "gateway": "2001:67c:1562:8007::1",
|
||||
+ "iface": "eth0",
|
||||
+ "metric": "50",
|
||||
+}
|
||||
+VMW_IPV6_NETDEV_ADDR = {
|
||||
+ "ip": "fd42:baa2:3dd:17a:216:3eff:fe16:db54/64",
|
||||
+ "scope6": "global",
|
||||
+}
|
||||
+
|
||||
+
|
||||
+def generate_test_netdev_data(ipv4=None, ipv6=None):
|
||||
+ ipv4 = ipv4 or []
|
||||
+ ipv6 = ipv6 or []
|
||||
+ return {
|
||||
+ "eth0": {
|
||||
+ "hwaddr": "00:16:3e:16:db:54",
|
||||
+ "ipv4": ipv4,
|
||||
+ "ipv6": ipv6,
|
||||
+ "up": True,
|
||||
+ },
|
||||
+ }
|
||||
+
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def common_patches():
|
||||
@@ -74,8 +113,8 @@ def common_patches():
|
||||
is_FreeBSD=mock.Mock(return_value=False),
|
||||
),
|
||||
mock.patch(
|
||||
- "cloudinit.sources.DataSourceVMware.netifaces.interfaces",
|
||||
- return_value=[],
|
||||
+ "cloudinit.netinfo.netdev_info",
|
||||
+ return_value={},
|
||||
),
|
||||
mock.patch(
|
||||
"cloudinit.sources.DataSourceVMware.getfqdn",
|
||||
@@ -152,6 +191,124 @@ class TestDataSourceVMware(CiTestCase):
|
||||
host_info[DataSourceVMware.LOCAL_IPV6] == "2001:db8::::::8888"
|
||||
)
|
||||
|
||||
+ # TODO migrate this entire test suite to pytest then parameterize
|
||||
+ @mock.patch("cloudinit.netinfo.route_info")
|
||||
+ @mock.patch("cloudinit.netinfo.netdev_info")
|
||||
+ def test_get_default_ip_addrs_ipv4only(
|
||||
+ self,
|
||||
+ m_netdev_info,
|
||||
+ m_route_info,
|
||||
+ ):
|
||||
+ """Test get_default_ip_addrs use cases"""
|
||||
+ m_route_info.return_value = {
|
||||
+ "ipv4": [VMW_IPV4_ROUTEINFO],
|
||||
+ "ipv6": [],
|
||||
+ }
|
||||
+ m_netdev_info.return_value = generate_test_netdev_data(
|
||||
+ ipv4=[VMW_IPV4_NETDEV_ADDR]
|
||||
+ )
|
||||
+ ipv4, ipv6 = DataSourceVMware.get_default_ip_addrs()
|
||||
+ self.assertEqual(ipv4, "10.85.130.116")
|
||||
+ self.assertEqual(ipv6, None)
|
||||
+
|
||||
+ @mock.patch("cloudinit.netinfo.route_info")
|
||||
+ @mock.patch("cloudinit.netinfo.netdev_info")
|
||||
+ def test_get_default_ip_addrs_ipv6only(
|
||||
+ self,
|
||||
+ m_netdev_info,
|
||||
+ m_route_info,
|
||||
+ ):
|
||||
+ m_route_info.return_value = {
|
||||
+ "ipv4": [],
|
||||
+ "ipv6": [VMW_IPV6_ROUTEINFO],
|
||||
+ }
|
||||
+ m_netdev_info.return_value = generate_test_netdev_data(
|
||||
+ ipv6=[VMW_IPV6_NETDEV_ADDR]
|
||||
+ )
|
||||
+ ipv4, ipv6 = DataSourceVMware.get_default_ip_addrs()
|
||||
+ self.assertEqual(ipv4, None)
|
||||
+ self.assertEqual(ipv6, "fd42:baa2:3dd:17a:216:3eff:fe16:db54/64")
|
||||
+
|
||||
+ @mock.patch("cloudinit.netinfo.route_info")
|
||||
+ @mock.patch("cloudinit.netinfo.netdev_info")
|
||||
+ def test_get_default_ip_addrs_dualstack(
|
||||
+ self,
|
||||
+ m_netdev_info,
|
||||
+ m_route_info,
|
||||
+ ):
|
||||
+ m_route_info.return_value = {
|
||||
+ "ipv4": [VMW_IPV4_ROUTEINFO],
|
||||
+ "ipv6": [VMW_IPV6_ROUTEINFO],
|
||||
+ }
|
||||
+ m_netdev_info.return_value = generate_test_netdev_data(
|
||||
+ ipv4=[VMW_IPV4_NETDEV_ADDR],
|
||||
+ ipv6=[VMW_IPV6_NETDEV_ADDR],
|
||||
+ )
|
||||
+ ipv4, ipv6 = DataSourceVMware.get_default_ip_addrs()
|
||||
+ self.assertEqual(ipv4, "10.85.130.116")
|
||||
+ self.assertEqual(ipv6, "fd42:baa2:3dd:17a:216:3eff:fe16:db54/64")
|
||||
+
|
||||
+ @mock.patch("cloudinit.netinfo.route_info")
|
||||
+ @mock.patch("cloudinit.netinfo.netdev_info")
|
||||
+ def test_get_default_ip_addrs_multiaddr(
|
||||
+ self,
|
||||
+ m_netdev_info,
|
||||
+ m_route_info,
|
||||
+ ):
|
||||
+ m_route_info.return_value = {
|
||||
+ "ipv4": [VMW_IPV4_ROUTEINFO],
|
||||
+ "ipv6": [],
|
||||
+ }
|
||||
+ m_netdev_info.return_value = generate_test_netdev_data(
|
||||
+ ipv4=[
|
||||
+ VMW_IPV4_NETDEV_ADDR,
|
||||
+ {
|
||||
+ "bcast": "10.85.131.255",
|
||||
+ "ip": "10.85.131.117",
|
||||
+ "mask": "255.255.255.0",
|
||||
+ "scope": "global",
|
||||
+ },
|
||||
+ ],
|
||||
+ ipv6=[
|
||||
+ VMW_IPV6_NETDEV_ADDR,
|
||||
+ {
|
||||
+ "ip": "fe80::216:3eff:fe16:db54/64",
|
||||
+ "scope6": "link",
|
||||
+ },
|
||||
+ ],
|
||||
+ )
|
||||
+ ipv4, ipv6 = DataSourceVMware.get_default_ip_addrs()
|
||||
+ self.assertEqual(ipv4, None)
|
||||
+ self.assertEqual(ipv6, None)
|
||||
+
|
||||
+ @mock.patch("cloudinit.netinfo.route_info")
|
||||
+ @mock.patch("cloudinit.netinfo.netdev_info")
|
||||
+ def test_get_default_ip_addrs_nodefault(
|
||||
+ self,
|
||||
+ m_netdev_info,
|
||||
+ m_route_info,
|
||||
+ ):
|
||||
+ m_route_info.return_value = {
|
||||
+ "ipv4": [
|
||||
+ {
|
||||
+ "destination": "185.125.188.0",
|
||||
+ "flags": "G",
|
||||
+ "gateway": "10.85.130.1",
|
||||
+ "genmask": "0.0.0.255",
|
||||
+ "iface": "eth0",
|
||||
+ "metric": "50",
|
||||
+ },
|
||||
+ ],
|
||||
+ "ipv6": [],
|
||||
+ }
|
||||
+ m_netdev_info.return_value = generate_test_netdev_data(
|
||||
+ ipv4=[VMW_IPV4_NETDEV_ADDR],
|
||||
+ ipv6=[VMW_IPV6_NETDEV_ADDR],
|
||||
+ )
|
||||
+ ipv4, ipv6 = DataSourceVMware.get_default_ip_addrs()
|
||||
+ self.assertEqual(ipv4, None)
|
||||
+ self.assertEqual(ipv6, None)
|
||||
+
|
||||
@mock.patch("cloudinit.sources.DataSourceVMware.get_host_info")
|
||||
def test_wait_on_network(self, m_fn):
|
||||
metadata = {
|
||||
diff --git a/tools/build-on-netbsd b/tools/build-on-netbsd
|
||||
index 0d4eb58b..b743d591 100755
|
||||
--- a/tools/build-on-netbsd
|
||||
+++ b/tools/build-on-netbsd
|
||||
@@ -19,7 +19,6 @@ pkgs="
|
||||
${py_prefix}-oauthlib
|
||||
${py_prefix}-requests
|
||||
${py_prefix}-setuptools
|
||||
- ${py_prefix}-netifaces
|
||||
${py_prefix}-yaml
|
||||
${py_prefix}-jsonschema
|
||||
sudo
|
||||
diff --git a/tools/build-on-openbsd b/tools/build-on-openbsd
|
||||
index 948ebeb8..09262aff 100755
|
||||
--- a/tools/build-on-openbsd
|
||||
+++ b/tools/build-on-openbsd
|
||||
@@ -16,7 +16,6 @@ pkgs="
|
||||
py3-configobj
|
||||
py3-jinja2
|
||||
py3-jsonschema
|
||||
- py3-netifaces
|
||||
py3-oauthlib
|
||||
py3-requests
|
||||
py3-setuptools
|
||||
diff --git a/tox.ini b/tox.ini
|
||||
index 34b87d01..473e937c 100644
|
||||
--- a/tox.ini
|
||||
+++ b/tox.ini
|
||||
@@ -194,7 +194,6 @@ deps =
|
||||
requests==2.18.4
|
||||
jsonpatch==1.16
|
||||
jsonschema==2.6.0
|
||||
- netifaces==0.10.4
|
||||
# test-requirements
|
||||
pytest==3.3.2
|
||||
pytest-cov==2.5.1
|
||||
--
|
||||
2.39.3
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue