b954b98a1c
- Backport cloud-init PR to add proper NetworkManager support - Add patch to prefer NetworkManager
2443 lines
82 KiB
Diff
2443 lines
82 KiB
Diff
From a60d61ea50e59b40bc1341831c622cac89bcc3df Mon Sep 17 00:00:00 2001
|
|
From: Lubomir Rintel <lkundrak@v3.sk>
|
|
Date: Mon, 31 Jan 2022 09:44:03 +0100
|
|
Subject: [PATCH 1/6] Replace invalid IP addresses in test
|
|
|
|
My guess is that they're invalid by accident, unless the tests were really
|
|
meant to test garbage-in-garbage-out behavior.
|
|
---
|
|
tests/unittests/test_net.py | 20 ++++++++++----------
|
|
1 file changed, 10 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
index 47e4ba00b0..61e4fce039 100644
|
|
--- a/tests/unittests/test_net.py
|
|
+++ b/tests/unittests/test_net.py
|
|
@@ -2419,10 +2419,10 @@
|
|
- type: static
|
|
address: 2001:1::1/92
|
|
routes:
|
|
- - gateway: 2001:67c:1562:1
|
|
+ - gateway: 2001:67c:1562::1
|
|
network: 2001:67c:1
|
|
netmask: "ffff:ffff::"
|
|
- - gateway: 3001:67c:1562:1
|
|
+ - gateway: 3001:67c:1562::1
|
|
network: 3001:67c:1
|
|
netmask: "ffff:ffff::"
|
|
metric: 10000
|
|
@@ -2467,10 +2467,10 @@
|
|
- to: 10.1.3.0/24
|
|
via: 192.168.0.3
|
|
- to: 2001:67c:1/32
|
|
- via: 2001:67c:1562:1
|
|
+ via: 2001:67c:1562::1
|
|
- metric: 10000
|
|
to: 3001:67c:1/32
|
|
- via: 3001:67c:1562:1
|
|
+ via: 3001:67c:1562::1
|
|
"""
|
|
),
|
|
"expected_eni": textwrap.dedent(
|
|
@@ -2530,11 +2530,11 @@
|
|
# control-alias bond0
|
|
iface bond0 inet6 static
|
|
address 2001:1::1/92
|
|
- post-up route add -A inet6 2001:67c:1/32 gw 2001:67c:1562:1 || true
|
|
- pre-down route del -A inet6 2001:67c:1/32 gw 2001:67c:1562:1 || true
|
|
- post-up route add -A inet6 3001:67c:1/32 gw 3001:67c:1562:1 metric 10000 \
|
|
+ post-up route add -A inet6 2001:67c:1/32 gw 2001:67c:1562::1 || true
|
|
+ pre-down route del -A inet6 2001:67c:1/32 gw 2001:67c:1562::1 || true
|
|
+ post-up route add -A inet6 3001:67c:1/32 gw 3001:67c:1562::1 metric 10000 \
|
|
|| true
|
|
- pre-down route del -A inet6 3001:67c:1/32 gw 3001:67c:1562:1 metric 10000 \
|
|
+ pre-down route del -A inet6 3001:67c:1/32 gw 3001:67c:1562::1 metric 10000 \
|
|
|| true
|
|
"""
|
|
),
|
|
@@ -2712,8 +2712,8 @@
|
|
"""\
|
|
# Created by cloud-init on instance boot automatically, do not edit.
|
|
#
|
|
- 2001:67c:1/32 via 2001:67c:1562:1 dev bond0
|
|
- 3001:67c:1/32 via 3001:67c:1562:1 metric 10000 dev bond0
|
|
+ 2001:67c:1/32 via 2001:67c:1562::1 dev bond0
|
|
+ 3001:67c:1/32 via 3001:67c:1562::1 metric 10000 dev bond0
|
|
"""
|
|
),
|
|
"route-bond0": textwrap.dedent(
|
|
|
|
From 76f7bb9d8b64a653cb53face69351b059e30bc0b Mon Sep 17 00:00:00 2001
|
|
From: Lubomir Rintel <lkundrak@v3.sk>
|
|
Date: Wed, 2 Feb 2022 12:48:32 +0100
|
|
Subject: [PATCH 2/6] Use a tad shorter gateway address in net test
|
|
|
|
Now that we fixed it (previous commit) by adding a missing colon, one line
|
|
ends up one character longer that flake8 would have preferred it to be.
|
|
|
|
Shorten it to appease the all too unforgiving CI gods.
|
|
---
|
|
tests/unittests/test_net.py | 18 +++++++++---------
|
|
1 file changed, 9 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
index 61e4fce039..517e13123d 100644
|
|
--- a/tests/unittests/test_net.py
|
|
+++ b/tests/unittests/test_net.py
|
|
@@ -2422,7 +2422,7 @@
|
|
- gateway: 2001:67c:1562::1
|
|
network: 2001:67c:1
|
|
netmask: "ffff:ffff::"
|
|
- - gateway: 3001:67c:1562::1
|
|
+ - gateway: 3001:67c:15::1
|
|
network: 3001:67c:1
|
|
netmask: "ffff:ffff::"
|
|
metric: 10000
|
|
@@ -2470,7 +2470,7 @@
|
|
via: 2001:67c:1562::1
|
|
- metric: 10000
|
|
to: 3001:67c:1/32
|
|
- via: 3001:67c:1562::1
|
|
+ via: 3001:67c:15::1
|
|
"""
|
|
),
|
|
"expected_eni": textwrap.dedent(
|
|
@@ -2532,9 +2532,9 @@
|
|
address 2001:1::1/92
|
|
post-up route add -A inet6 2001:67c:1/32 gw 2001:67c:1562::1 || true
|
|
pre-down route del -A inet6 2001:67c:1/32 gw 2001:67c:1562::1 || true
|
|
- post-up route add -A inet6 3001:67c:1/32 gw 3001:67c:1562::1 metric 10000 \
|
|
+ post-up route add -A inet6 3001:67c:1/32 gw 3001:67c:15::1 metric 10000 \
|
|
|| true
|
|
- pre-down route del -A inet6 3001:67c:1/32 gw 3001:67c:1562::1 metric 10000 \
|
|
+ pre-down route del -A inet6 3001:67c:1/32 gw 3001:67c:15::1 metric 10000 \
|
|
|| true
|
|
"""
|
|
),
|
|
@@ -2577,8 +2577,8 @@
|
|
- to: 2001:67c:1562:8007::1/64
|
|
via: 2001:67c:1562:8007::aac:40b2
|
|
- metric: 10000
|
|
- to: 3001:67c:1562:8007::1/64
|
|
- via: 3001:67c:1562:8007::aac:40b2
|
|
+ to: 3001:67c:15:8007::1/64
|
|
+ via: 3001:67c:15:8007::aac:40b2
|
|
"""
|
|
),
|
|
"expected_netplan-v2": textwrap.dedent(
|
|
@@ -2610,8 +2610,8 @@
|
|
- to: 2001:67c:1562:8007::1/64
|
|
via: 2001:67c:1562:8007::aac:40b2
|
|
- metric: 10000
|
|
- to: 3001:67c:1562:8007::1/64
|
|
- via: 3001:67c:1562:8007::aac:40b2
|
|
+ to: 3001:67c:15:8007::1/64
|
|
+ via: 3001:67c:15:8007::aac:40b2
|
|
ethernets:
|
|
eth0:
|
|
match:
|
|
@@ -2713,7 +2713,7 @@
|
|
# Created by cloud-init on instance boot automatically, do not edit.
|
|
#
|
|
2001:67c:1/32 via 2001:67c:1562::1 dev bond0
|
|
- 3001:67c:1/32 via 3001:67c:1562::1 metric 10000 dev bond0
|
|
+ 3001:67c:1/32 via 3001:67c:15::1 metric 10000 dev bond0
|
|
"""
|
|
),
|
|
"route-bond0": textwrap.dedent(
|
|
|
|
From df4f8b35f4e469e123349896be0804300a9786af Mon Sep 17 00:00:00 2001
|
|
From: Lubomir Rintel <lkundrak@v3.sk>
|
|
Date: Fri, 28 Jan 2022 17:30:13 +0100
|
|
Subject: [PATCH 3/6] Revert "net: Make sysconfig renderer compatible with
|
|
Network Manager."
|
|
|
|
Firstly, this relies upon the fact that you can get ifcfg support by adding
|
|
it to NetworkManager.conf. That is not guarranteed and certianly will not
|
|
be case in future.
|
|
|
|
Secondly, cloud-init always generates configuration with
|
|
NM_CONTROLLED=no, so the generated ifcfg files are no good for
|
|
NetworkManager. Fedora patches around this by just removing those lines
|
|
in their cloud-init package.
|
|
|
|
Let's remove this and add a proper NetworkManager support later on
|
|
instead.
|
|
|
|
This reverts commit 3861102fcaf47a882516d8b6daab518308eb3086.
|
|
---
|
|
cloudinit/net/sysconfig.py | 37 +-----------
|
|
tests/unittests/test_net.py | 113 +++++-------------------------------
|
|
2 files changed, 17 insertions(+), 133 deletions(-)
|
|
|
|
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
|
|
index ba85c4f673..d866c9aa79 100644
|
|
--- a/cloudinit/net/sysconfig.py
|
|
+++ b/cloudinit/net/sysconfig.py
|
|
@@ -5,8 +5,6 @@
|
|
import os
|
|
import re
|
|
|
|
-from configobj import ConfigObj
|
|
-
|
|
from cloudinit import log as logging
|
|
from cloudinit import subp, util
|
|
from cloudinit.distros.parsers import networkmanager_conf, resolv_conf
|
|
@@ -66,24 +64,6 @@ def _quote_value(value):
|
|
return value
|
|
|
|
|
|
-def enable_ifcfg_rh(path):
|
|
- """Add ifcfg-rh to NetworkManager.cfg plugins if main section is present"""
|
|
- config = ConfigObj(path)
|
|
- if "main" in config:
|
|
- if "plugins" in config["main"]:
|
|
- if "ifcfg-rh" in config["main"]["plugins"]:
|
|
- return
|
|
- else:
|
|
- config["main"]["plugins"] = []
|
|
-
|
|
- if isinstance(config["main"]["plugins"], list):
|
|
- config["main"]["plugins"].append("ifcfg-rh")
|
|
- else:
|
|
- config["main"]["plugins"] = [config["main"]["plugins"], "ifcfg-rh"]
|
|
- config.write()
|
|
- LOG.debug("Enabled ifcfg-rh NetworkManager plugins")
|
|
-
|
|
-
|
|
class ConfigMap(object):
|
|
"""Sysconfig like dictionary object."""
|
|
|
|
@@ -1032,8 +1012,6 @@ def render_network_state(self, network_state, templates=None, target=None):
|
|
netrules_content = self._render_persistent_net(network_state)
|
|
netrules_path = subp.target_path(target, self.netrules_path)
|
|
util.write_file(netrules_path, netrules_content, file_mode)
|
|
- if available_nm(target=target):
|
|
- enable_ifcfg_rh(subp.target_path(target, path=NM_CFG_FILE))
|
|
|
|
sysconfig_path = subp.target_path(target, templates.get("control"))
|
|
# Distros configuring /etc/sysconfig/network as a file e.g. Centos
|
|
@@ -1063,14 +1041,9 @@ def _supported_vlan_names(rdev, vid):
|
|
|
|
|
|
def available(target=None):
|
|
- sysconfig = available_sysconfig(target=target)
|
|
- nm = available_nm(target=target)
|
|
- return util.system_info()["variant"] in KNOWN_DISTROS and any(
|
|
- [nm, sysconfig]
|
|
- )
|
|
-
|
|
+ if not util.system_info()["variant"] in KNOWN_DISTROS:
|
|
+ return False
|
|
|
|
-def available_sysconfig(target=None):
|
|
expected = ["ifup", "ifdown"]
|
|
search = ["/sbin", "/usr/sbin"]
|
|
for p in expected:
|
|
@@ -1087,10 +1060,4 @@ def available_sysconfig(target=None):
|
|
return False
|
|
|
|
|
|
-def available_nm(target=None):
|
|
- if not os.path.isfile(subp.target_path(target, path=NM_CFG_FILE)):
|
|
- return False
|
|
- return True
|
|
-
|
|
-
|
|
# vi: ts=4 expandtab
|
|
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
index 517e13123d..c473c79142 100644
|
|
--- a/tests/unittests/test_net.py
|
|
+++ b/tests/unittests/test_net.py
|
|
@@ -3522,7 +3522,6 @@ class TestRhelSysConfigRendering(CiTestCase):
|
|
|
|
with_logs = True
|
|
|
|
- nm_cfg_file = "/etc/NetworkManager/NetworkManager.conf"
|
|
scripts_dir = "/etc/sysconfig/network-scripts"
|
|
header = (
|
|
"# Created by cloud-init on instance boot automatically, "
|
|
@@ -4100,78 +4099,6 @@ def test_wakeonlan_enabled_config_v2(self):
|
|
self._compare_files_to_expected(entry[self.expected_name], found)
|
|
self._assert_headers(found)
|
|
|
|
- def test_check_ifcfg_rh(self):
|
|
- """ifcfg-rh plugin is added NetworkManager.conf if conf present."""
|
|
- render_dir = self.tmp_dir()
|
|
- nm_cfg = subp.target_path(render_dir, path=self.nm_cfg_file)
|
|
- util.ensure_dir(os.path.dirname(nm_cfg))
|
|
-
|
|
- # write a template nm.conf, note plugins is a list here
|
|
- with open(nm_cfg, "w") as fh:
|
|
- fh.write("# test_check_ifcfg_rh\n[main]\nplugins=foo,bar\n")
|
|
- self.assertTrue(os.path.exists(nm_cfg))
|
|
-
|
|
- # render and read
|
|
- entry = NETWORK_CONFIGS["small"]
|
|
- found = self._render_and_read(
|
|
- network_config=yaml.load(entry["yaml"]), dir=render_dir
|
|
- )
|
|
- self._compare_files_to_expected(entry[self.expected_name], found)
|
|
- self._assert_headers(found)
|
|
-
|
|
- # check ifcfg-rh is in the 'plugins' list
|
|
- config = sysconfig.ConfigObj(nm_cfg)
|
|
- self.assertIn("ifcfg-rh", config["main"]["plugins"])
|
|
-
|
|
- def test_check_ifcfg_rh_plugins_string(self):
|
|
- """ifcfg-rh plugin is append when plugins is a string."""
|
|
- render_dir = self.tmp_path("render")
|
|
- os.makedirs(render_dir)
|
|
- nm_cfg = subp.target_path(render_dir, path=self.nm_cfg_file)
|
|
- util.ensure_dir(os.path.dirname(nm_cfg))
|
|
-
|
|
- # write a template nm.conf, note plugins is a value here
|
|
- util.write_file(nm_cfg, "# test_check_ifcfg_rh\n[main]\nplugins=foo\n")
|
|
-
|
|
- # render and read
|
|
- entry = NETWORK_CONFIGS["small"]
|
|
- found = self._render_and_read(
|
|
- network_config=yaml.load(entry["yaml"]), dir=render_dir
|
|
- )
|
|
- self._compare_files_to_expected(entry[self.expected_name], found)
|
|
- self._assert_headers(found)
|
|
-
|
|
- # check raw content has plugin
|
|
- nm_file_content = util.load_file(nm_cfg)
|
|
- self.assertIn("ifcfg-rh", nm_file_content)
|
|
-
|
|
- # check ifcfg-rh is in the 'plugins' list
|
|
- config = sysconfig.ConfigObj(nm_cfg)
|
|
- self.assertIn("ifcfg-rh", config["main"]["plugins"])
|
|
-
|
|
- def test_check_ifcfg_rh_plugins_no_plugins(self):
|
|
- """enable_ifcfg_plugin creates plugins value if missing."""
|
|
- render_dir = self.tmp_path("render")
|
|
- os.makedirs(render_dir)
|
|
- nm_cfg = subp.target_path(render_dir, path=self.nm_cfg_file)
|
|
- util.ensure_dir(os.path.dirname(nm_cfg))
|
|
-
|
|
- # write a template nm.conf, note plugins is missing
|
|
- util.write_file(nm_cfg, "# test_check_ifcfg_rh\n[main]\n")
|
|
- self.assertTrue(os.path.exists(nm_cfg))
|
|
-
|
|
- # render and read
|
|
- entry = NETWORK_CONFIGS["small"]
|
|
- found = self._render_and_read(
|
|
- network_config=yaml.load(entry["yaml"]), dir=render_dir
|
|
- )
|
|
- self._compare_files_to_expected(entry[self.expected_name], found)
|
|
- self._assert_headers(found)
|
|
-
|
|
- # check ifcfg-rh is in the 'plugins' list
|
|
- config = sysconfig.ConfigObj(nm_cfg)
|
|
- self.assertIn("ifcfg-rh", config["main"]["plugins"])
|
|
-
|
|
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)
|
|
@@ -6164,60 +6091,50 @@ def test_dhcpv6_reject_ra_config_v2(self, m_chown):
|
|
|
|
class TestRenderersSelect:
|
|
@pytest.mark.parametrize(
|
|
- "renderer_selected,netplan,eni,nm,scfg,sys,networkd",
|
|
+ "renderer_selected,netplan,eni,sys,networkd",
|
|
(
|
|
- # -netplan -ifupdown -nm -scfg -sys raises error
|
|
+ # -netplan -ifupdown -sys raises error
|
|
(
|
|
net.RendererNotFoundError,
|
|
False,
|
|
False,
|
|
False,
|
|
False,
|
|
- False,
|
|
- False,
|
|
),
|
|
- # -netplan +ifupdown -nm -scfg -sys selects eni
|
|
- ("eni", False, True, False, False, False, False),
|
|
- # +netplan +ifupdown -nm -scfg -sys selects eni
|
|
- ("eni", True, True, False, False, False, False),
|
|
- # +netplan -ifupdown -nm -scfg -sys selects netplan
|
|
- ("netplan", True, False, False, False, False, False),
|
|
+ # -netplan +ifupdown -sys selects eni
|
|
+ ("eni", False, True, False, False),
|
|
+ # +netplan +ifupdown -sys selects eni
|
|
+ ("eni", True, True, False, False),
|
|
+ # +netplan -ifupdown -sys selects netplan
|
|
+ ("netplan", True, False, False, False),
|
|
# Ubuntu with Network-Manager installed
|
|
- # +netplan -ifupdown +nm -scfg -sys selects netplan
|
|
- ("netplan", True, False, True, False, False, False),
|
|
+ # +netplan -ifupdown -sys selects netplan
|
|
+ ("netplan", True, False, False, False),
|
|
# Centos/OpenSuse with Network-Manager installed selects sysconfig
|
|
- # -netplan -ifupdown +nm -scfg +sys selects netplan
|
|
- ("sysconfig", False, False, True, False, True, False),
|
|
- # -netplan -ifupdown -nm -scfg -sys +networkd selects networkd
|
|
- ("networkd", False, False, False, False, False, True),
|
|
+ # -netplan -ifupdown +sys selects netplan
|
|
+ ("sysconfig", False, False, True, False),
|
|
+ # -netplan -ifupdown -sys +networkd selects networkd
|
|
+ ("networkd", False, False, False, True),
|
|
),
|
|
)
|
|
@mock.patch("cloudinit.net.renderers.networkd.available")
|
|
@mock.patch("cloudinit.net.renderers.netplan.available")
|
|
@mock.patch("cloudinit.net.renderers.sysconfig.available")
|
|
- @mock.patch("cloudinit.net.renderers.sysconfig.available_sysconfig")
|
|
- @mock.patch("cloudinit.net.renderers.sysconfig.available_nm")
|
|
@mock.patch("cloudinit.net.renderers.eni.available")
|
|
def test_valid_renderer_from_defaults_depending_on_availability(
|
|
self,
|
|
m_eni_avail,
|
|
- m_nm_avail,
|
|
- m_scfg_avail,
|
|
m_sys_avail,
|
|
m_netplan_avail,
|
|
m_networkd_avail,
|
|
renderer_selected,
|
|
netplan,
|
|
eni,
|
|
- nm,
|
|
- scfg,
|
|
sys,
|
|
networkd,
|
|
):
|
|
"""Assert proper renderer per DEFAULT_PRIORITY given availability."""
|
|
m_eni_avail.return_value = eni # ifupdown pkg presence
|
|
- m_nm_avail.return_value = nm # network-manager presence
|
|
- m_scfg_avail.return_value = scfg # sysconfig presence
|
|
m_sys_avail.return_value = sys # sysconfig/ifup/down presence
|
|
m_netplan_avail.return_value = netplan # netplan presence
|
|
m_networkd_avail.return_value = networkd # networkd presence
|
|
@@ -6277,7 +6194,7 @@ def test_select_none_found_raises(self, m_eni_avail, m_sysc_avail):
|
|
priority=["sysconfig", "eni"],
|
|
)
|
|
|
|
- @mock.patch("cloudinit.net.sysconfig.available_sysconfig")
|
|
+ @mock.patch("cloudinit.net.sysconfig.available")
|
|
@mock.patch("cloudinit.util.system_info")
|
|
def test_sysconfig_available_uses_variant_mapping(self, m_info, m_avail):
|
|
m_avail.return_value = True
|
|
|
|
From c47045d47464d73004dac2a4938aaddda7f25f19 Mon Sep 17 00:00:00 2001
|
|
From: Lubomir Rintel <lkundrak@v3.sk>
|
|
Date: Mon, 31 Jan 2022 09:17:24 +0100
|
|
Subject: [PATCH 4/6] Fix a couple of comments
|
|
|
|
The comments above
|
|
test_valid_renderer_from_defaults_depending_on_availability()
|
|
were not quite right.
|
|
---
|
|
tests/unittests/test_net.py | 14 ++++++--------
|
|
1 file changed, 6 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
index c473c79142..b409c13c93 100644
|
|
--- a/tests/unittests/test_net.py
|
|
+++ b/tests/unittests/test_net.py
|
|
@@ -6093,7 +6093,7 @@ class TestRenderersSelect:
|
|
@pytest.mark.parametrize(
|
|
"renderer_selected,netplan,eni,sys,networkd",
|
|
(
|
|
- # -netplan -ifupdown -sys raises error
|
|
+ # -netplan -ifupdown -sys -networkd raises error
|
|
(
|
|
net.RendererNotFoundError,
|
|
False,
|
|
@@ -6101,17 +6101,15 @@ class TestRenderersSelect:
|
|
False,
|
|
False,
|
|
),
|
|
- # -netplan +ifupdown -sys selects eni
|
|
+ # -netplan +ifupdown -sys -networkd selects eni
|
|
("eni", False, True, False, False),
|
|
- # +netplan +ifupdown -sys selects eni
|
|
+ # +netplan +ifupdown -sys -networkd selects eni
|
|
("eni", True, True, False, False),
|
|
- # +netplan -ifupdown -sys selects netplan
|
|
+ # +netplan -ifupdown -sys -networkd selects netplan
|
|
("netplan", True, False, False, False),
|
|
- # Ubuntu with Network-Manager installed
|
|
- # +netplan -ifupdown -sys selects netplan
|
|
+ # +netplan -ifupdown -sys -networkd selects netplan
|
|
("netplan", True, False, False, False),
|
|
- # Centos/OpenSuse with Network-Manager installed selects sysconfig
|
|
- # -netplan -ifupdown +sys selects netplan
|
|
+ # -netplan -ifupdown +sys -networkd selects sysconfig
|
|
("sysconfig", False, False, True, False),
|
|
# -netplan -ifupdown -sys +networkd selects networkd
|
|
("networkd", False, False, False, True),
|
|
|
|
From 0cec899e804eb1a609f09d355384a3f41bf13575 Mon Sep 17 00:00:00 2001
|
|
From: Lubomir Rintel <lkundrak@v3.sk>
|
|
Date: Fri, 28 Jan 2022 12:29:25 +0100
|
|
Subject: [PATCH 5/6] Add NetworkManager renderer
|
|
|
|
This generates native NetworkManager keyfiles as opposed to relying on
|
|
ifcfg compatibility, because the later has been long deprecated and is
|
|
going to go away from new Fedora installations.
|
|
---
|
|
cloudinit/cmd/devel/net_convert.py | 14 +-
|
|
cloudinit/net/activators.py | 25 +-
|
|
cloudinit/net/network_manager.py | 377 +++++++++++++++++++++++++
|
|
cloudinit/net/renderers.py | 3 +
|
|
tests/unittests/test_net_activators.py | 93 +++++-
|
|
5 files changed, 484 insertions(+), 28 deletions(-)
|
|
create mode 100644 cloudinit/net/network_manager.py
|
|
|
|
diff --git a/cloudinit/cmd/devel/net_convert.py b/cloudinit/cmd/devel/net_convert.py
|
|
index 18b1e7ff78..647fe07b09 100755
|
|
--- a/cloudinit/cmd/devel/net_convert.py
|
|
+++ b/cloudinit/cmd/devel/net_convert.py
|
|
@@ -7,7 +7,14 @@
|
|
import sys
|
|
|
|
from cloudinit import distros, log, safeyaml
|
|
-from cloudinit.net import eni, netplan, network_state, networkd, sysconfig
|
|
+from cloudinit.net import (
|
|
+ eni,
|
|
+ netplan,
|
|
+ network_manager,
|
|
+ network_state,
|
|
+ networkd,
|
|
+ sysconfig,
|
|
+)
|
|
from cloudinit.sources import DataSourceAzure as azure
|
|
from cloudinit.sources import DataSourceOVF as ovf
|
|
from cloudinit.sources.helpers import openstack
|
|
@@ -74,7 +81,7 @@ def get_parser(parser=None):
|
|
parser.add_argument(
|
|
"-O",
|
|
"--output-kind",
|
|
- choices=["eni", "netplan", "networkd", "sysconfig"],
|
|
+ choices=["eni", "netplan", "networkd", "sysconfig", "network-manager"],
|
|
required=True,
|
|
help="The network config format to emit",
|
|
)
|
|
@@ -148,6 +155,9 @@ def handle_args(name, args):
|
|
elif args.output_kind == "sysconfig":
|
|
r_cls = sysconfig.Renderer
|
|
config = distro.renderer_configs.get("sysconfig")
|
|
+ elif args.output_kind == "network-manager":
|
|
+ r_cls = network_manager.Renderer
|
|
+ config = distro.renderer_configs.get("network-manager")
|
|
else:
|
|
raise RuntimeError("Invalid output_kind")
|
|
|
|
diff --git a/cloudinit/net/activators.py b/cloudinit/net/activators.py
|
|
index e80c26df38..edbc0c065b 100644
|
|
--- a/cloudinit/net/activators.py
|
|
+++ b/cloudinit/net/activators.py
|
|
@@ -1,15 +1,14 @@
|
|
# This file is part of cloud-init. See LICENSE file for license information.
|
|
import logging
|
|
-import os
|
|
from abc import ABC, abstractmethod
|
|
from typing import Iterable, List, Type
|
|
|
|
from cloudinit import subp, util
|
|
from cloudinit.net.eni import available as eni_available
|
|
from cloudinit.net.netplan import available as netplan_available
|
|
+from cloudinit.net.network_manager import available as nm_available
|
|
from cloudinit.net.network_state import NetworkState
|
|
from cloudinit.net.networkd import available as networkd_available
|
|
-from cloudinit.net.sysconfig import NM_CFG_FILE
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
@@ -124,20 +123,24 @@ def bring_down_interface(device_name: str) -> bool:
|
|
class NetworkManagerActivator(NetworkActivator):
|
|
@staticmethod
|
|
def available(target=None) -> bool:
|
|
- """Return true if network manager can be used on this system."""
|
|
- config_present = os.path.isfile(
|
|
- subp.target_path(target, path=NM_CFG_FILE)
|
|
- )
|
|
- nmcli_present = subp.which("nmcli", target=target)
|
|
- return config_present and bool(nmcli_present)
|
|
+ """Return true if NetworkManager can be used on this system."""
|
|
+ return nm_available(target=target)
|
|
|
|
@staticmethod
|
|
def bring_up_interface(device_name: str) -> bool:
|
|
- """Bring up interface using nmcli.
|
|
+ """Bring up connection using nmcli.
|
|
|
|
Return True is successful, otherwise return False
|
|
"""
|
|
- cmd = ["nmcli", "connection", "up", "ifname", device_name]
|
|
+ from cloudinit.net.network_manager import conn_filename
|
|
+
|
|
+ filename = conn_filename(device_name)
|
|
+ cmd = ["nmcli", "connection", "load", filename]
|
|
+ if _alter_interface(cmd, device_name):
|
|
+ cmd = ["nmcli", "connection", "up", "filename", filename]
|
|
+ else:
|
|
+ _alter_interface(["nmcli", "connection", "reload"], device_name)
|
|
+ cmd = ["nmcli", "connection", "up", "ifname", device_name]
|
|
return _alter_interface(cmd, device_name)
|
|
|
|
@staticmethod
|
|
@@ -146,7 +149,7 @@ def bring_down_interface(device_name: str) -> bool:
|
|
|
|
Return True is successful, otherwise return False
|
|
"""
|
|
- cmd = ["nmcli", "connection", "down", device_name]
|
|
+ cmd = ["nmcli", "device", "disconnect", device_name]
|
|
return _alter_interface(cmd, device_name)
|
|
|
|
|
|
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
|
|
new file mode 100644
|
|
index 0000000000..79b0fe0bf6
|
|
--- /dev/null
|
|
+++ b/cloudinit/net/network_manager.py
|
|
@@ -0,0 +1,377 @@
|
|
+# Copyright 2022 Red Hat, Inc.
|
|
+#
|
|
+# Author: Lubomir Rintel <lkundrak@v3.sk>
|
|
+# Fixes and suggestions contributed by James Falcon, Neal Gompa,
|
|
+# Zbigniew Jędrzejewski-Szmek and Emanuele Giuseppe Esposito.
|
|
+#
|
|
+# This file is part of cloud-init. See LICENSE file for license information.
|
|
+
|
|
+import configparser
|
|
+import io
|
|
+import itertools
|
|
+import os
|
|
+import uuid
|
|
+
|
|
+from cloudinit import log as logging
|
|
+from cloudinit import subp, util
|
|
+
|
|
+from . import renderer
|
|
+from .network_state import is_ipv6_addr, subnet_is_ipv6
|
|
+
|
|
+NM_RUN_DIR = "/etc/NetworkManager"
|
|
+NM_LIB_DIR = "/usr/lib/NetworkManager"
|
|
+LOG = logging.getLogger(__name__)
|
|
+
|
|
+
|
|
+class NMConnection:
|
|
+ """Represents a NetworkManager connection profile."""
|
|
+
|
|
+ def __init__(self, con_id):
|
|
+ """
|
|
+ Initializes the connection with some very basic properties,
|
|
+ notably the UUID so that the connection can be referred to.
|
|
+ """
|
|
+
|
|
+ # Chosen by fair dice roll
|
|
+ CI_NM_UUID = uuid.UUID("a3924cb8-09e0-43e9-890b-77972a800108")
|
|
+
|
|
+ self.config = configparser.ConfigParser()
|
|
+ # Identity option name mapping, to achieve case sensitivity
|
|
+ self.config.optionxform = str
|
|
+
|
|
+ self.config["connection"] = {
|
|
+ "id": f"cloud-init {con_id}",
|
|
+ "uuid": str(uuid.uuid5(CI_NM_UUID, con_id)),
|
|
+ }
|
|
+
|
|
+ # This is not actually used anywhere, but may be useful in future
|
|
+ self.config["user"] = {
|
|
+ "org.freedesktop.NetworkManager.origin": "cloud-init"
|
|
+ }
|
|
+
|
|
+ def _set_default(self, section, option, value):
|
|
+ """
|
|
+ Sets a property unless it's already set, ensuring the section
|
|
+ exists.
|
|
+ """
|
|
+
|
|
+ if not self.config.has_section(section):
|
|
+ self.config[section] = {}
|
|
+ if not self.config.has_option(section, option):
|
|
+ self.config[section][option] = value
|
|
+
|
|
+ def _set_ip_method(self, family, subnet_type):
|
|
+ """
|
|
+ Ensures there's appropriate [ipv4]/[ipv6] for given family
|
|
+ appropriate for given configuration type
|
|
+ """
|
|
+
|
|
+ method_map = {
|
|
+ "static": "manual",
|
|
+ "dhcp6": "dhcp",
|
|
+ "ipv6_slaac": "auto",
|
|
+ "ipv6_dhcpv6-stateless": "auto",
|
|
+ "ipv6_dhcpv6-stateful": "auto",
|
|
+ "dhcp4": "auto",
|
|
+ "dhcp": "auto",
|
|
+ }
|
|
+
|
|
+ # Ensure we got an [ipvX] section
|
|
+ self._set_default(family, "method", "disabled")
|
|
+
|
|
+ try:
|
|
+ method = method_map[subnet_type]
|
|
+ except KeyError:
|
|
+ # What else can we do
|
|
+ method = "auto"
|
|
+ self.config[family]["may-fail"] = "true"
|
|
+
|
|
+ # Make sure we don't "downgrade" the method in case
|
|
+ # we got conflicting subnets (e.g. static along with dhcp)
|
|
+ if self.config[family]["method"] == "dhcp":
|
|
+ return
|
|
+ if self.config[family]["method"] == "auto" and method == "manual":
|
|
+ return
|
|
+
|
|
+ self.config[family]["method"] = method
|
|
+ self._set_default(family, "may-fail", "false")
|
|
+ if family == "ipv6":
|
|
+ self._set_default(family, "addr-gen-mode", "stable-privacy")
|
|
+
|
|
+ def _add_numbered(self, section, key_prefix, value):
|
|
+ """
|
|
+ Adds a numbered property, such as address<n> or route<n>, ensuring
|
|
+ the appropriate value gets used for <n>.
|
|
+ """
|
|
+
|
|
+ for index in itertools.count(1):
|
|
+ key = f"{key_prefix}{index}"
|
|
+ if not self.config.has_option(section, key):
|
|
+ self.config[section][key] = value
|
|
+ break
|
|
+
|
|
+ def _add_address(self, family, subnet):
|
|
+ """
|
|
+ Adds an ipv[46]address<n> property.
|
|
+ """
|
|
+
|
|
+ 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.
|
|
+ """
|
|
+
|
|
+ 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):
|
|
+ """
|
|
+ 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_addr(dns) else "ipv4"
|
|
+ self._set_default(family, "method", "disabled")
|
|
+
|
|
+ self._set_default(family, "dns", "")
|
|
+ self.config[family]["dns"] = self.config[family]["dns"] + dns + ";"
|
|
+
|
|
+ def _add_dns_search(self, family, dns_search):
|
|
+ """
|
|
+ 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) + ";"
|
|
+ )
|
|
+
|
|
+ def con_uuid(self):
|
|
+ """
|
|
+ Returns the connection UUID
|
|
+ """
|
|
+ return self.config["connection"]["uuid"]
|
|
+
|
|
+ def valid(self):
|
|
+ """
|
|
+ Can this be serialized into a meaningful connection profile?
|
|
+ """
|
|
+ return self.config.has_option("connection", "type")
|
|
+
|
|
+ @staticmethod
|
|
+ def mac_addr(addr):
|
|
+ """
|
|
+ Sanitize a MAC address.
|
|
+ """
|
|
+ return addr.replace("-", ":").upper()
|
|
+
|
|
+ def render_interface(self, iface, renderer):
|
|
+ """
|
|
+ Integrate information from network state interface information
|
|
+ into the connection. Most of the work is done here.
|
|
+ """
|
|
+
|
|
+ # Initialize type & connectivity
|
|
+ _type_map = {
|
|
+ "physical": "ethernet",
|
|
+ "vlan": "vlan",
|
|
+ "bond": "bond",
|
|
+ "bridge": "bridge",
|
|
+ "infiniband": "infiniband",
|
|
+ "loopback": None,
|
|
+ }
|
|
+
|
|
+ if_type = _type_map[iface["type"]]
|
|
+ if if_type is None:
|
|
+ return
|
|
+ if "bond-master" in iface:
|
|
+ slave_type = "bond"
|
|
+ else:
|
|
+ slave_type = None
|
|
+
|
|
+ self.config["connection"]["type"] = if_type
|
|
+ if slave_type is not None:
|
|
+ self.config["connection"]["slave-type"] = slave_type
|
|
+ self.config["connection"]["master"] = renderer.con_ref(
|
|
+ iface[slave_type + "-master"]
|
|
+ )
|
|
+
|
|
+ # Add type specific-section
|
|
+ self.config[if_type] = {}
|
|
+
|
|
+ # These are the interface properties that map nicely
|
|
+ # to NetworkManager properties
|
|
+ _prop_map = {
|
|
+ "bond": {
|
|
+ "mode": "bond-mode",
|
|
+ "miimon": "bond_miimon",
|
|
+ "xmit_hash_policy": "bond-xmit-hash-policy",
|
|
+ "num_grat_arp": "bond-num-grat-arp",
|
|
+ "downdelay": "bond-downdelay",
|
|
+ "updelay": "bond-updelay",
|
|
+ "fail_over_mac": "bond-fail-over-mac",
|
|
+ "primary_reselect": "bond-primary-reselect",
|
|
+ "primary": "bond-primary",
|
|
+ },
|
|
+ "bridge": {
|
|
+ "stp": "bridge_stp",
|
|
+ "priority": "bridge_bridgeprio",
|
|
+ },
|
|
+ "vlan": {
|
|
+ "id": "vlan_id",
|
|
+ },
|
|
+ "ethernet": {},
|
|
+ "infiniband": {},
|
|
+ }
|
|
+
|
|
+ device_mtu = iface["mtu"]
|
|
+ ipv4_mtu = None
|
|
+
|
|
+ # Deal with Layer 3 configuration
|
|
+ for subnet in iface["subnets"]:
|
|
+ family = "ipv6" if subnet_is_ipv6(subnet) else "ipv4"
|
|
+
|
|
+ self._set_ip_method(family, subnet["type"])
|
|
+ if "address" in subnet:
|
|
+ self._add_address(family, subnet)
|
|
+ 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:
|
|
+ for nameserver in subnet["dns_nameservers"]:
|
|
+ self._add_nameserver(nameserver)
|
|
+ if "dns_search" in subnet:
|
|
+ self._add_dns_search(family, subnet["dns_search"])
|
|
+ if family == "ipv4" and "mtu" in subnet:
|
|
+ ipv4_mtu = subnet["mtu"]
|
|
+
|
|
+ if ipv4_mtu is None:
|
|
+ ipv4_mtu = device_mtu
|
|
+ if not ipv4_mtu == device_mtu:
|
|
+ LOG.warning(
|
|
+ "Network config: ignoring %s device-level mtu:%s"
|
|
+ " because ipv4 subnet-level mtu:%s provided.",
|
|
+ iface["name"],
|
|
+ device_mtu,
|
|
+ ipv4_mtu,
|
|
+ )
|
|
+
|
|
+ # Parse type-specific properties
|
|
+ for nm_prop, key in _prop_map[if_type].items():
|
|
+ if key not in iface:
|
|
+ continue
|
|
+ if iface[key] is None:
|
|
+ continue
|
|
+ if isinstance(iface[key], bool):
|
|
+ self.config[if_type][nm_prop] = (
|
|
+ "true" if iface[key] else "false"
|
|
+ )
|
|
+ else:
|
|
+ self.config[if_type][nm_prop] = str(iface[key])
|
|
+
|
|
+ # These ones need special treatment
|
|
+ if if_type == "ethernet":
|
|
+ if iface["wakeonlan"] is True:
|
|
+ # NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC
|
|
+ self.config["ethernet"]["wake-on-lan"] = str(0x40)
|
|
+ if ipv4_mtu is not None:
|
|
+ self.config["ethernet"]["mtu"] = str(ipv4_mtu)
|
|
+ if iface["mac_address"] is not None:
|
|
+ self.config["ethernet"]["mac-address"] = self.mac_addr(
|
|
+ iface["mac_address"]
|
|
+ )
|
|
+ if if_type == "vlan" and "vlan-raw-device" in iface:
|
|
+ self.config["vlan"]["parent"] = renderer.con_ref(
|
|
+ iface["vlan-raw-device"]
|
|
+ )
|
|
+ if if_type == "bridge":
|
|
+ # Bridge is ass-backwards compared to bond
|
|
+ for port in iface["bridge_ports"]:
|
|
+ port = renderer.get_conn(port)
|
|
+ port._set_default("connection", "slave-type", "bridge")
|
|
+ port._set_default("connection", "master", self.con_uuid())
|
|
+ if iface["mac_address"] is not None:
|
|
+ self.config["bridge"]["mac-address"] = self.mac_addr(
|
|
+ iface["mac_address"]
|
|
+ )
|
|
+ if if_type == "infiniband" and ipv4_mtu is not None:
|
|
+ self.config["infiniband"]["transport-mode"] = "datagram"
|
|
+ self.config["infiniband"]["mtu"] = str(ipv4_mtu)
|
|
+ if iface["mac_address"] is not None:
|
|
+ self.config["infiniband"]["mac-address"] = self.mac_addr(
|
|
+ iface["mac_address"]
|
|
+ )
|
|
+
|
|
+ # Finish up
|
|
+ if if_type == "bridge" or not self.config.has_option(
|
|
+ if_type, "mac-address"
|
|
+ ):
|
|
+ self.config["connection"]["interface-name"] = iface["name"]
|
|
+
|
|
+ def dump(self):
|
|
+ """
|
|
+ Stringify.
|
|
+ """
|
|
+
|
|
+ buf = io.StringIO()
|
|
+ self.config.write(buf, space_around_delimiters=False)
|
|
+ header = "# Generated by cloud-init. Changes will be lost.\n\n"
|
|
+ return header + buf.getvalue()
|
|
+
|
|
+
|
|
+class Renderer(renderer.Renderer):
|
|
+ """Renders network information in a NetworkManager keyfile format."""
|
|
+
|
|
+ def __init__(self, config=None):
|
|
+ self.connections = {}
|
|
+
|
|
+ def get_conn(self, con_id):
|
|
+ return self.connections[con_id]
|
|
+
|
|
+ def con_ref(self, con_id):
|
|
+ if con_id in self.connections:
|
|
+ return self.connections[con_id].con_uuid()
|
|
+ else:
|
|
+ # Well, what can we do...
|
|
+ return con_id
|
|
+
|
|
+ def render_network_state(self, network_state, templates=None, target=None):
|
|
+ # First pass makes sure there's NMConnections for all known
|
|
+ # 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"])
|
|
+
|
|
+ # Now render the actual interface configuration
|
|
+ for iface in network_state.iter_interfaces():
|
|
+ conn = self.connections[iface["name"]]
|
|
+ conn.render_interface(iface, self)
|
|
+
|
|
+ # And finally write the files
|
|
+ for con_id, conn in self.connections.items():
|
|
+ if not conn.valid():
|
|
+ continue
|
|
+ name = conn_filename(con_id, target)
|
|
+ util.write_file(name, conn.dump(), 0o600)
|
|
+
|
|
+
|
|
+def 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 available(target=None):
|
|
+ target_nm_dir = subp.target_path(target, NM_LIB_DIR)
|
|
+ return os.path.exists(target_nm_dir)
|
|
+
|
|
+
|
|
+# vi: ts=4 expandtab
|
|
diff --git a/cloudinit/net/renderers.py b/cloudinit/net/renderers.py
|
|
index c755f04c1c..7edc34b55a 100644
|
|
--- a/cloudinit/net/renderers.py
|
|
+++ b/cloudinit/net/renderers.py
|
|
@@ -8,6 +8,7 @@
|
|
freebsd,
|
|
netbsd,
|
|
netplan,
|
|
+ network_manager,
|
|
networkd,
|
|
openbsd,
|
|
renderer,
|
|
@@ -19,6 +20,7 @@
|
|
"freebsd": freebsd,
|
|
"netbsd": netbsd,
|
|
"netplan": netplan,
|
|
+ "network-manager": network_manager,
|
|
"networkd": networkd,
|
|
"openbsd": openbsd,
|
|
"sysconfig": sysconfig,
|
|
@@ -28,6 +30,7 @@
|
|
"eni",
|
|
"sysconfig",
|
|
"netplan",
|
|
+ "network-manager",
|
|
"freebsd",
|
|
"netbsd",
|
|
"openbsd",
|
|
diff --git a/tests/unittests/test_net_activators.py b/tests/unittests/test_net_activators.py
|
|
index 3c29e2f752..4525c49c1b 100644
|
|
--- a/tests/unittests/test_net_activators.py
|
|
+++ b/tests/unittests/test_net_activators.py
|
|
@@ -41,18 +41,20 @@
|
|
|
|
@pytest.fixture
|
|
def available_mocks():
|
|
- mocks = namedtuple("Mocks", "m_which, m_file")
|
|
+ mocks = namedtuple("Mocks", "m_which, m_file, m_exists")
|
|
with patch("cloudinit.subp.which", return_value=True) as m_which:
|
|
with patch("os.path.isfile", return_value=True) as m_file:
|
|
- yield mocks(m_which, m_file)
|
|
+ with patch("os.path.exists", return_value=True) as m_exists:
|
|
+ yield mocks(m_which, m_file, m_exists)
|
|
|
|
|
|
@pytest.fixture
|
|
def unavailable_mocks():
|
|
- mocks = namedtuple("Mocks", "m_which, m_file")
|
|
+ mocks = namedtuple("Mocks", "m_which, m_file, m_exists")
|
|
with patch("cloudinit.subp.which", return_value=False) as m_which:
|
|
with patch("os.path.isfile", return_value=False) as m_file:
|
|
- yield mocks(m_which, m_file)
|
|
+ with patch("os.path.exists", return_value=False) as m_exists:
|
|
+ yield mocks(m_which, m_file, m_exists)
|
|
|
|
|
|
class TestSearchAndSelect:
|
|
@@ -113,10 +115,6 @@ def test_none_available(self, unavailable_mocks):
|
|
(("netplan",), {"search": ["/usr/sbin", "/sbin"], "target": None}),
|
|
]
|
|
|
|
-NETWORK_MANAGER_AVAILABLE_CALLS = [
|
|
- (("nmcli",), {"target": None}),
|
|
-]
|
|
-
|
|
NETWORKD_AVAILABLE_CALLS = [
|
|
(("ip",), {"search": ["/usr/sbin", "/bin"], "target": None}),
|
|
(("systemctl",), {"search": ["/usr/sbin", "/bin"], "target": None}),
|
|
@@ -128,7 +126,6 @@ def test_none_available(self, unavailable_mocks):
|
|
[
|
|
(IfUpDownActivator, IF_UP_DOWN_AVAILABLE_CALLS),
|
|
(NetplanActivator, NETPLAN_AVAILABLE_CALLS),
|
|
- (NetworkManagerActivator, NETWORK_MANAGER_AVAILABLE_CALLS),
|
|
(NetworkdActivator, NETWORKD_AVAILABLE_CALLS),
|
|
],
|
|
)
|
|
@@ -144,8 +141,72 @@ def test_available(self, activator, available_calls, available_mocks):
|
|
]
|
|
|
|
NETWORK_MANAGER_BRING_UP_CALL_LIST = [
|
|
- ((["nmcli", "connection", "up", "ifname", "eth0"],), {}),
|
|
- ((["nmcli", "connection", "up", "ifname", "eth1"],), {}),
|
|
+ (
|
|
+ (
|
|
+ [
|
|
+ "nmcli",
|
|
+ "connection",
|
|
+ "load",
|
|
+ "".join(
|
|
+ [
|
|
+ "/etc/NetworkManager/system-connections",
|
|
+ "/cloud-init-eth0.nmconnection",
|
|
+ ]
|
|
+ ),
|
|
+ ],
|
|
+ ),
|
|
+ {},
|
|
+ ),
|
|
+ (
|
|
+ (
|
|
+ [
|
|
+ "nmcli",
|
|
+ "connection",
|
|
+ "up",
|
|
+ "filename",
|
|
+ "".join(
|
|
+ [
|
|
+ "/etc/NetworkManager/system-connections",
|
|
+ "/cloud-init-eth0.nmconnection",
|
|
+ ]
|
|
+ ),
|
|
+ ],
|
|
+ ),
|
|
+ {},
|
|
+ ),
|
|
+ (
|
|
+ (
|
|
+ [
|
|
+ "nmcli",
|
|
+ "connection",
|
|
+ "load",
|
|
+ "".join(
|
|
+ [
|
|
+ "/etc/NetworkManager/system-connections",
|
|
+ "/cloud-init-eth1.nmconnection",
|
|
+ ]
|
|
+ ),
|
|
+ ],
|
|
+ ),
|
|
+ {},
|
|
+ ),
|
|
+ (
|
|
+ (
|
|
+ [
|
|
+ "nmcli",
|
|
+ "connection",
|
|
+ "up",
|
|
+ "filename",
|
|
+ "".join(
|
|
+ [
|
|
+ "/etc/NetworkManager/system-connections",
|
|
+ "/cloud-init-eth1.nmconnection",
|
|
+ ]
|
|
+ ),
|
|
+ ],
|
|
+ ),
|
|
+ {},
|
|
+ ),
|
|
]
|
|
|
|
NETWORKD_BRING_UP_CALL_LIST = [
|
|
@@ -169,9 +230,11 @@ class TestActivatorsBringUp:
|
|
def test_bring_up_interface(
|
|
self, m_subp, activator, expected_call_list, available_mocks
|
|
):
|
|
+ index = 0
|
|
activator.bring_up_interface("eth0")
|
|
- assert len(m_subp.call_args_list) == 1
|
|
- assert m_subp.call_args_list[0] == expected_call_list[0]
|
|
+ for call in m_subp.call_args_list:
|
|
+ assert call == expected_call_list[index]
|
|
+ index += 1
|
|
|
|
@patch("cloudinit.subp.subp", return_value=("", ""))
|
|
def test_bring_up_interfaces(
|
|
@@ -208,8 +271,8 @@ def test_bring_up_all_interfaces_v2(
|
|
]
|
|
|
|
NETWORK_MANAGER_BRING_DOWN_CALL_LIST = [
|
|
- ((["nmcli", "connection", "down", "eth0"],), {}),
|
|
- ((["nmcli", "connection", "down", "eth1"],), {}),
|
|
+ ((["nmcli", "device", "disconnect", "eth0"],), {}),
|
|
+ ((["nmcli", "device", "disconnect", "eth1"],), {}),
|
|
]
|
|
|
|
NETWORKD_BRING_DOWN_CALL_LIST = [
|
|
|
|
From 07e503391d4750a938fedb7ec9240b12af2b0896 Mon Sep 17 00:00:00 2001
|
|
From: Lubomir Rintel <lkundrak@v3.sk>
|
|
Date: Fri, 28 Jan 2022 17:36:43 +0100
|
|
Subject: [PATCH 6/6] Add unit tests for the NetworkManager network renderer
|
|
|
|
The test fixture is based upon what NetworkManager would generate when
|
|
reading in the legacy ifcfg (sysconfig) files.
|
|
---
|
|
tests/unittests/test_net.py | 1121 ++++++++++++++++++++++++++++++++++-
|
|
1 file changed, 1107 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
|
|
index b409c13c93..9552ac12f7 100644
|
|
--- a/tests/unittests/test_net.py
|
|
+++ b/tests/unittests/test_net.py
|
|
@@ -21,6 +21,7 @@
|
|
interface_has_own_mac,
|
|
natural_sort_key,
|
|
netplan,
|
|
+ network_manager,
|
|
network_state,
|
|
networkd,
|
|
renderers,
|
|
@@ -612,6 +613,37 @@
|
|
),
|
|
),
|
|
],
|
|
+ "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
|
|
+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
|
|
+
|
|
+""".lstrip(),
|
|
+ ),
|
|
+ ],
|
|
},
|
|
{
|
|
"in_data": {
|
|
@@ -1078,6 +1110,50 @@
|
|
USERCTL=no"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-eth1.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth1
|
|
+ uuid=3c50eb47-7260-5a6d-801d-bd4f587d6b58
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=CF:D6:AF:48:E8:80
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth99.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth99
|
|
+ uuid=b1b88000-1f03-5360-8377-1a2205efffb4
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=C0:D6:9F:2C:E8:80
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+ address1=192.168.21.3/24
|
|
+ route1=0.0.0.0/0,65.61.151.37
|
|
+ dns=8.8.8.8;8.8.4.4;
|
|
+ dns-search=barley.maas;sach.maas;
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
"yaml": textwrap.dedent(
|
|
"""
|
|
version: 1
|
|
@@ -1150,6 +1226,34 @@
|
|
STARTMODE=auto"""
|
|
)
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ [ipv6]
|
|
+ method=dhcp
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
"yaml": textwrap.dedent(
|
|
"""\
|
|
version: 1
|
|
@@ -1253,6 +1357,37 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mtu=9000
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.14.2/24
|
|
+
|
|
+ [ipv6]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+ address1=2001:1::1/64
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"v6_and_v4": {
|
|
"expected_sysconfig_opensuse": {
|
|
@@ -1263,6 +1398,34 @@
|
|
STARTMODE=auto"""
|
|
)
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv6]
|
|
+ method=dhcp
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
"yaml": textwrap.dedent(
|
|
"""\
|
|
version: 1
|
|
@@ -1336,6 +1499,30 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv6]
|
|
+ method=dhcp
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"dhcpv6_accept_ra": {
|
|
"expected_eni": textwrap.dedent(
|
|
@@ -1543,6 +1730,30 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv6]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"static6": {
|
|
"yaml": textwrap.dedent(
|
|
@@ -1631,6 +1842,30 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv6]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"dhcpv6_stateful": {
|
|
"expected_eni": textwrap.dedent(
|
|
@@ -1730,6 +1965,29 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
"yaml_v2": textwrap.dedent(
|
|
"""\
|
|
version: 2
|
|
@@ -1783,6 +2041,30 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-iface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init iface0
|
|
+ uuid=8ddfba48-857c-5e86-ac09-1b43eae0bf70
|
|
+ type=ethernet
|
|
+ interface-name=iface0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ wake-on-lan=64
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
"yaml_v2": textwrap.dedent(
|
|
"""\
|
|
version: 2
|
|
@@ -2231,6 +2513,254 @@
|
|
USERCTL=no"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-eth3.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth3
|
|
+ uuid=b7e95dda-7746-5bf8-bf33-6e5f3c926790
|
|
+ type=ethernet
|
|
+ slave-type=bridge
|
|
+ master=dee46ce4-af7a-5e7c-aa08-b25533ae9213
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=66:BB:9F:2C:E8:80
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth5.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth5
|
|
+ uuid=5fda13c7-9942-5e90-a41b-1d043bd725dc
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=98:BB:9F:2C:E8:8A
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-ib0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init ib0
|
|
+ uuid=11a1dda7-78b4-5529-beba-d9b5f549ad7b
|
|
+ type=infiniband
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [infiniband]
|
|
+ transport-mode=datagram
|
|
+ mtu=9000
|
|
+ mac-address=A0:00:02:20:FE:80:00:00:00:00:00:00:EC:0D:9A:03:00:15:E2:C1
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.200.7/24
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-bond0.200.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init bond0.200
|
|
+ uuid=88984a9c-ff22-5233-9267-86315e0acaa7
|
|
+ type=vlan
|
|
+ interface-name=bond0.200
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [vlan]
|
|
+ id=200
|
|
+ parent=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "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
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=C0:D6:9F:2C:E8:80
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth4.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth4
|
|
+ uuid=e27e4959-fb50-5580-b9a4-2073554627b9
|
|
+ type=ethernet
|
|
+ slave-type=bridge
|
|
+ master=dee46ce4-af7a-5e7c-aa08-b25533ae9213
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=98:BB:9F:2C:E8:80
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth1.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth1
|
|
+ uuid=3c50eb47-7260-5a6d-801d-bd4f587d6b58
|
|
+ type=ethernet
|
|
+ slave-type=bond
|
|
+ master=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=AA:D6:9F:2C:E8:80
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-br0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init br0
|
|
+ uuid=dee46ce4-af7a-5e7c-aa08-b25533ae9213
|
|
+ type=bridge
|
|
+ interface-name=br0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [bridge]
|
|
+ stp=false
|
|
+ priority=22
|
|
+ mac-address=BB:BB:BB:BB:BB:AA
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.14.2/24
|
|
+
|
|
+ [ipv6]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+ address1=2001:1::1/64
|
|
+ route1=::/0,2001:4800:78ff:1b::1
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth0.101.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth0.101
|
|
+ uuid=b5acec5e-db80-5935-8b02-0d5619fc42bf
|
|
+ type=vlan
|
|
+ interface-name=eth0.101
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [vlan]
|
|
+ id=101
|
|
+ parent=1dd9a779-d327-56e1-8454-c65e2556c12c
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.0.2/24
|
|
+ gateway=192.168.0.1
|
|
+ dns=192.168.0.10;10.23.23.134;
|
|
+ dns-search=barley.maas;sacchromyces.maas;brettanomyces.maas;
|
|
+ address2=192.168.2.10/24
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-bond0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init bond0
|
|
+ uuid=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+ type=bond
|
|
+ interface-name=bond0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [bond]
|
|
+ mode=active-backup
|
|
+ miimon=100
|
|
+ xmit_hash_policy=layer3+4
|
|
+
|
|
+ [ipv6]
|
|
+ method=dhcp
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth2.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth2
|
|
+ uuid=5559a242-3421-5fdd-896e-9cb8313d5804
|
|
+ type=ethernet
|
|
+ slave-type=bond
|
|
+ master=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=C0:BB:9F:2C:E8:80
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
"yaml": textwrap.dedent(
|
|
"""
|
|
version: 1
|
|
@@ -2737,6 +3267,88 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-bond0s0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init bond0s0
|
|
+ uuid=09d0b5b9-67e7-5577-a1af-74d1cf17a71e
|
|
+ type=ethernet
|
|
+ slave-type=bond
|
|
+ master=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=AA:BB:CC:DD:E8:00
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-bond0s1.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init bond0s1
|
|
+ uuid=4d9aca96-b515-5630-ad83-d13daac7f9d0
|
|
+ type=ethernet
|
|
+ slave-type=bond
|
|
+ master=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=AA:BB:CC:DD:E8:01
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-bond0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init bond0
|
|
+ uuid=54317911-f840-516b-a10d-82cb4c1f075c
|
|
+ type=bond
|
|
+ interface-name=bond0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [bond]
|
|
+ mode=active-backup
|
|
+ miimon=100
|
|
+ xmit_hash_policy=layer3+4
|
|
+ num_grat_arp=5
|
|
+ downdelay=10
|
|
+ updelay=20
|
|
+ fail_over_mac=active
|
|
+ primary_reselect=always
|
|
+ primary=bond0s0
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.0.2/24
|
|
+ gateway=192.168.0.1
|
|
+ route1=10.1.3.0/24,192.168.0.3
|
|
+ address2=192.168.1.2/24
|
|
+
|
|
+ [ipv6]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+ address1=2001:1::1/92
|
|
+ route1=2001:67c:1/32,2001:67c:1562::1
|
|
+ route2=3001:67c:1/32,3001:67c:15::1
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"vlan": {
|
|
"yaml": textwrap.dedent(
|
|
@@ -2822,6 +3434,58 @@
|
|
VLAN=yes"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-en0.99.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init en0.99
|
|
+ uuid=f594e2ed-f107-51df-b225-1dc530a5356b
|
|
+ type=vlan
|
|
+ interface-name=en0.99
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [vlan]
|
|
+ id=99
|
|
+ parent=e0ca478b-8d84-52ab-8fae-628482c629b5
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.2.2/24
|
|
+ address2=192.168.1.2/24
|
|
+ gateway=192.168.1.1
|
|
+
|
|
+ [ipv6]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+ address1=2001:1::bbbb/96
|
|
+ route1=::/0,2001:1::1
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-en0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init en0
|
|
+ uuid=e0ca478b-8d84-52ab-8fae-628482c629b5
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=AA:BB:CC:DD:E8:00
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"bridge": {
|
|
"yaml": textwrap.dedent(
|
|
@@ -2931,6 +3595,82 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "expected_network_manager": {
|
|
+ "cloud-init-br0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init br0
|
|
+ uuid=dee46ce4-af7a-5e7c-aa08-b25533ae9213
|
|
+ type=bridge
|
|
+ interface-name=br0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [bridge]
|
|
+ stp=false
|
|
+ priority=22
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.2.2/24
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "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
|
|
+ type=ethernet
|
|
+ slave-type=bridge
|
|
+ master=dee46ce4-af7a-5e7c-aa08-b25533ae9213
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=52:54:00:12:34:00
|
|
+
|
|
+ [ipv6]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+ address1=2001:1::100/96
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth1.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth1
|
|
+ uuid=3c50eb47-7260-5a6d-801d-bd4f587d6b58
|
|
+ type=ethernet
|
|
+ slave-type=bridge
|
|
+ master=dee46ce4-af7a-5e7c-aa08-b25533ae9213
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=52:54:00:12:34:01
|
|
+
|
|
+ [ipv6]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ addr-gen-mode=stable-privacy
|
|
+ address1=2001:1::101/96
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
"manual": {
|
|
"yaml": textwrap.dedent(
|
|
@@ -3062,6 +3802,73 @@
|
|
"""
|
|
),
|
|
},
|
|
+ "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
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=52:54:00:12:34:00
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=192.168.1.2/24
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth1.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth1
|
|
+ uuid=3c50eb47-7260-5a6d-801d-bd4f587d6b58
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mtu=1480
|
|
+ mac-address=52:54:00:12:34:AA
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=true
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ "cloud-init-eth2.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth2
|
|
+ uuid=5559a242-3421-5fdd-896e-9cb8313d5804
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=52:54:00:12:34:FF
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=true
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
},
|
|
}
|
|
|
|
@@ -4654,6 +5461,281 @@ def test_render_v6_and_v4(self):
|
|
self._assert_headers(found)
|
|
|
|
|
|
+@mock.patch(
|
|
+ "cloudinit.net.is_openvswitch_internal_interface",
|
|
+ mock.Mock(return_value=False),
|
|
+)
|
|
+class TestNetworkManagerRendering(CiTestCase):
|
|
+
|
|
+ with_logs = True
|
|
+
|
|
+ scripts_dir = "/etc/NetworkManager/system-connections"
|
|
+
|
|
+ expected_name = "expected_network_manager"
|
|
+
|
|
+ def _get_renderer(self):
|
|
+ return network_manager.Renderer()
|
|
+
|
|
+ def _render_and_read(self, network_config=None, state=None, dir=None):
|
|
+ if dir is None:
|
|
+ dir = self.tmp_dir()
|
|
+
|
|
+ if network_config:
|
|
+ ns = network_state.parse_net_config_data(network_config)
|
|
+ elif state:
|
|
+ ns = state
|
|
+ else:
|
|
+ raise ValueError("Expected data or state, got neither")
|
|
+
|
|
+ renderer = self._get_renderer()
|
|
+ renderer.render_network_state(ns, target=dir)
|
|
+ return dir2dict(dir)
|
|
+
|
|
+ def _compare_files_to_expected(self, expected, found):
|
|
+ orig_maxdiff = self.maxDiff
|
|
+ expected_d = dict(
|
|
+ (os.path.join(self.scripts_dir, k), v) for k, v in expected.items()
|
|
+ )
|
|
+
|
|
+ try:
|
|
+ self.maxDiff = None
|
|
+ self.assertEqual(expected_d, found)
|
|
+ finally:
|
|
+ self.maxDiff = orig_maxdiff
|
|
+
|
|
+ @mock.patch("cloudinit.net.util.get_cmdline", return_value="root=myroot")
|
|
+ @mock.patch("cloudinit.net.sys_dev_path")
|
|
+ @mock.patch("cloudinit.net.read_sys_net")
|
|
+ @mock.patch("cloudinit.net.get_devicelist")
|
|
+ def test_default_generation(
|
|
+ self,
|
|
+ mock_get_devicelist,
|
|
+ mock_read_sys_net,
|
|
+ mock_sys_dev_path,
|
|
+ m_get_cmdline,
|
|
+ ):
|
|
+ tmp_dir = self.tmp_dir()
|
|
+ _setup_test(
|
|
+ tmp_dir, mock_get_devicelist, mock_read_sys_net, mock_sys_dev_path
|
|
+ )
|
|
+
|
|
+ network_cfg = net.generate_fallback_config()
|
|
+ ns = network_state.parse_net_config_data(
|
|
+ network_cfg, skip_broken=False
|
|
+ )
|
|
+
|
|
+ render_dir = os.path.join(tmp_dir, "render")
|
|
+ os.makedirs(render_dir)
|
|
+
|
|
+ renderer = self._get_renderer()
|
|
+ renderer.render_network_state(ns, target=render_dir)
|
|
+
|
|
+ found = dir2dict(render_dir)
|
|
+ self._compare_files_to_expected(
|
|
+ {
|
|
+ "cloud-init-eth1000.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init eth1000
|
|
+ uuid=8c517500-0c95-5308-9c8a-3092eebc44eb
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=07:1C:C6:75:A4:BE
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
+ found,
|
|
+ )
|
|
+
|
|
+ def test_openstack_rendering_samples(self):
|
|
+ for os_sample in OS_SAMPLES:
|
|
+ render_dir = self.tmp_dir()
|
|
+ ex_input = os_sample["in_data"]
|
|
+ ex_mac_addrs = os_sample["in_macs"]
|
|
+ network_cfg = openstack.convert_net_json(
|
|
+ ex_input, known_macs=ex_mac_addrs
|
|
+ )
|
|
+ ns = network_state.parse_net_config_data(
|
|
+ network_cfg, skip_broken=False
|
|
+ )
|
|
+ renderer = self._get_renderer()
|
|
+ # render a multiple times to simulate reboots
|
|
+ renderer.render_network_state(ns, target=render_dir)
|
|
+ renderer.render_network_state(ns, target=render_dir)
|
|
+ renderer.render_network_state(ns, target=render_dir)
|
|
+ for fn, expected_content in os_sample.get(self.expected_name, []):
|
|
+ with open(os.path.join(render_dir, fn)) as fh:
|
|
+ self.assertEqual(expected_content, fh.read())
|
|
+
|
|
+ def test_network_config_v1_samples(self):
|
|
+ ns = network_state.parse_net_config_data(CONFIG_V1_SIMPLE_SUBNET)
|
|
+ render_dir = self.tmp_path("render")
|
|
+ os.makedirs(render_dir)
|
|
+ renderer = self._get_renderer()
|
|
+ renderer.render_network_state(ns, target=render_dir)
|
|
+ found = dir2dict(render_dir)
|
|
+ self._compare_files_to_expected(
|
|
+ {
|
|
+ "cloud-init-interface0.nmconnection": textwrap.dedent(
|
|
+ """\
|
|
+ # Generated by cloud-init. Changes will be lost.
|
|
+
|
|
+ [connection]
|
|
+ id=cloud-init interface0
|
|
+ uuid=8b6862ed-dbd6-5830-93f7-a91451c13828
|
|
+ type=ethernet
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+ mac-address=52:54:00:12:34:00
|
|
+
|
|
+ [ipv4]
|
|
+ method=manual
|
|
+ may-fail=false
|
|
+ address1=10.0.2.15/24
|
|
+ gateway=10.0.2.2
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
+ found,
|
|
+ )
|
|
+
|
|
+ def test_config_with_explicit_loopback(self):
|
|
+ render_dir = self.tmp_path("render")
|
|
+ os.makedirs(render_dir)
|
|
+ ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
|
|
+ renderer = self._get_renderer()
|
|
+ renderer.render_network_state(ns, target=render_dir)
|
|
+ found = dir2dict(render_dir)
|
|
+ self._compare_files_to_expected(
|
|
+ {
|
|
+ "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
|
|
+ type=ethernet
|
|
+ interface-name=eth0
|
|
+
|
|
+ [user]
|
|
+ org.freedesktop.NetworkManager.origin=cloud-init
|
|
+
|
|
+ [ethernet]
|
|
+
|
|
+ [ipv4]
|
|
+ method=auto
|
|
+ may-fail=false
|
|
+
|
|
+ """
|
|
+ ),
|
|
+ },
|
|
+ found,
|
|
+ )
|
|
+
|
|
+ def test_bond_config(self):
|
|
+ entry = NETWORK_CONFIGS["bond"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_vlan_config(self):
|
|
+ entry = NETWORK_CONFIGS["vlan"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_bridge_config(self):
|
|
+ entry = NETWORK_CONFIGS["bridge"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_manual_config(self):
|
|
+ entry = NETWORK_CONFIGS["manual"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_all_config(self):
|
|
+ entry = NETWORK_CONFIGS["all"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+ self.assertNotIn(
|
|
+ "WARNING: Network config: ignoring eth0.101 device-level mtu",
|
|
+ self.logs.getvalue(),
|
|
+ )
|
|
+
|
|
+ def test_small_config(self):
|
|
+ entry = NETWORK_CONFIGS["small"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_v4_and_v6_static_config(self):
|
|
+ entry = NETWORK_CONFIGS["v4_and_v6_static"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+ expected_msg = (
|
|
+ "WARNING: Network config: ignoring iface0 device-level mtu:8999"
|
|
+ " because ipv4 subnet-level mtu:9000 provided."
|
|
+ )
|
|
+ self.assertIn(expected_msg, self.logs.getvalue())
|
|
+
|
|
+ def test_dhcpv6_only_config(self):
|
|
+ entry = NETWORK_CONFIGS["dhcpv6_only"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_simple_render_ipv6_slaac(self):
|
|
+ entry = NETWORK_CONFIGS["ipv6_slaac"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_dhcpv6_stateless_config(self):
|
|
+ entry = NETWORK_CONFIGS["dhcpv6_stateless"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_wakeonlan_disabled_config_v2(self):
|
|
+ entry = NETWORK_CONFIGS["wakeonlan_disabled"]
|
|
+ found = self._render_and_read(
|
|
+ network_config=yaml.load(entry["yaml_v2"])
|
|
+ )
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_wakeonlan_enabled_config_v2(self):
|
|
+ entry = NETWORK_CONFIGS["wakeonlan_enabled"]
|
|
+ found = self._render_and_read(
|
|
+ network_config=yaml.load(entry["yaml_v2"])
|
|
+ )
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_render_v4_and_v6(self):
|
|
+ entry = NETWORK_CONFIGS["v4_and_v6"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+ def test_render_v6_and_v4(self):
|
|
+ entry = NETWORK_CONFIGS["v6_and_v4"]
|
|
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
|
|
+ self._compare_files_to_expected(entry[self.expected_name], found)
|
|
+
|
|
+
|
|
+@mock.patch(
|
|
+ "cloudinit.net.is_openvswitch_internal_interface",
|
|
+ mock.Mock(return_value=False),
|
|
+)
|
|
class TestEniNetRendering(CiTestCase):
|
|
@mock.patch("cloudinit.net.util.get_cmdline", return_value="root=myroot")
|
|
@mock.patch("cloudinit.net.sys_dev_path")
|
|
@@ -6091,31 +7173,39 @@ def test_dhcpv6_reject_ra_config_v2(self, m_chown):
|
|
|
|
class TestRenderersSelect:
|
|
@pytest.mark.parametrize(
|
|
- "renderer_selected,netplan,eni,sys,networkd",
|
|
+ "renderer_selected,netplan,eni,sys,network_manager,networkd",
|
|
(
|
|
- # -netplan -ifupdown -sys -networkd raises error
|
|
+ # -netplan -ifupdown -sys -network-manager -networkd raises error
|
|
(
|
|
net.RendererNotFoundError,
|
|
False,
|
|
False,
|
|
False,
|
|
False,
|
|
+ False,
|
|
),
|
|
- # -netplan +ifupdown -sys -networkd selects eni
|
|
- ("eni", False, True, False, False),
|
|
- # +netplan +ifupdown -sys -networkd selects eni
|
|
- ("eni", True, True, False, False),
|
|
- # +netplan -ifupdown -sys -networkd selects netplan
|
|
- ("netplan", True, False, False, False),
|
|
- # +netplan -ifupdown -sys -networkd selects netplan
|
|
- ("netplan", True, False, False, False),
|
|
- # -netplan -ifupdown +sys -networkd selects sysconfig
|
|
- ("sysconfig", False, False, True, False),
|
|
- # -netplan -ifupdown -sys +networkd selects networkd
|
|
- ("networkd", False, False, False, True),
|
|
+ # -netplan +ifupdown -sys -nm -networkd selects eni
|
|
+ ("eni", False, True, False, False, False),
|
|
+ # +netplan +ifupdown -sys -nm -networkd selects eni
|
|
+ ("eni", True, True, False, False, False),
|
|
+ # +netplan -ifupdown -sys -nm -networkd selects netplan
|
|
+ ("netplan", True, False, False, False, False),
|
|
+ # +netplan -ifupdown -sys -nm -networkd selects netplan
|
|
+ ("netplan", True, False, False, False, False),
|
|
+ # -netplan -ifupdown +sys -nm -networkd selects sysconfig
|
|
+ ("sysconfig", False, False, True, False, False),
|
|
+ # -netplan -ifupdown +sys +nm -networkd selects sysconfig
|
|
+ ("sysconfig", False, False, True, True, False),
|
|
+ # -netplan -ifupdown -sys +nm -networkd selects nm
|
|
+ ("network-manager", False, False, False, True, False),
|
|
+ # -netplan -ifupdown -sys +nm +networkd selects nm
|
|
+ ("network-manager", False, False, False, True, True),
|
|
+ # -netplan -ifupdown -sys -nm +networkd selects networkd
|
|
+ ("networkd", False, False, False, False, True),
|
|
),
|
|
)
|
|
@mock.patch("cloudinit.net.renderers.networkd.available")
|
|
+ @mock.patch("cloudinit.net.renderers.network_manager.available")
|
|
@mock.patch("cloudinit.net.renderers.netplan.available")
|
|
@mock.patch("cloudinit.net.renderers.sysconfig.available")
|
|
@mock.patch("cloudinit.net.renderers.eni.available")
|
|
@@ -6124,17 +7214,20 @@ def test_valid_renderer_from_defaults_depending_on_availability(
|
|
m_eni_avail,
|
|
m_sys_avail,
|
|
m_netplan_avail,
|
|
+ m_network_manager_avail,
|
|
m_networkd_avail,
|
|
renderer_selected,
|
|
netplan,
|
|
eni,
|
|
sys,
|
|
+ network_manager,
|
|
networkd,
|
|
):
|
|
"""Assert proper renderer per DEFAULT_PRIORITY given availability."""
|
|
m_eni_avail.return_value = eni # ifupdown pkg presence
|
|
m_sys_avail.return_value = sys # sysconfig/ifup/down presence
|
|
m_netplan_avail.return_value = netplan # netplan presence
|
|
+ m_network_manager_avail.return_value = network_manager # NM presence
|
|
m_networkd_avail.return_value = networkd # networkd presence
|
|
if isinstance(renderer_selected, str):
|
|
(renderer_name, _rnd_class) = renderers.select(
|