summaryrefslogtreecommitdiff
path: root/tests/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unittests')
-rw-r--r--tests/unittests/test_cli.py2
-rw-r--r--tests/unittests/test_distros/test_netconfig.py99
-rw-r--r--tests/unittests/test_handler/test_handler_set_hostname.py26
-rw-r--r--tests/unittests/test_net.py244
-rw-r--r--tests/unittests/test_render_cloudcfg.py3
5 files changed, 362 insertions, 12 deletions
diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
index fbc6ec11..fdb4026c 100644
--- a/tests/unittests/test_cli.py
+++ b/tests/unittests/test_cli.py
@@ -225,7 +225,7 @@ class TestCLI(test_helpers.FilesystemMockingTestCase):
expected_doc_sections = [
'**Supported distros:** all',
('**Supported distros:** almalinux, alpine, centos, debian, '
- 'fedora, opensuse, rhel, rocky, sles, ubuntu'),
+ 'fedora, opensuse, photon, rhel, rocky, sles, ubuntu'),
'**Config schema**:\n **resize_rootfs:** (true/false/noblock)',
'**Examples**::\n\n runcmd:\n - [ ls, -l, / ]\n'
]
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
index a1df066a..562ee04a 100644
--- a/tests/unittests/test_distros/test_netconfig.py
+++ b/tests/unittests/test_distros/test_netconfig.py
@@ -2,6 +2,7 @@
import copy
import os
+import re
from io import StringIO
from textwrap import dedent
from unittest import mock
@@ -15,7 +16,6 @@ from cloudinit.tests.helpers import (
from cloudinit import subp
from cloudinit import util
-
BASE_NET_CFG = '''
auto lo
iface lo inet loopback
@@ -771,6 +771,103 @@ class TestNetCfgDistroArch(TestNetCfgDistroBase):
with_netplan=True)
+class TestNetCfgDistroPhoton(TestNetCfgDistroBase):
+
+ def setUp(self):
+ super(TestNetCfgDistroPhoton, self).setUp()
+ self.distro = self._get_distro('photon', renderers=['networkd'])
+
+ def create_conf_dict(self, contents):
+ content_dict = {}
+ for line in contents:
+ if line:
+ line = line.strip()
+ if line and re.search(r'^\[(.+)\]$', line):
+ content_dict[line] = []
+ key = line
+ elif line:
+ assert key
+ content_dict[key].append(line)
+
+ return content_dict
+
+ def compare_dicts(self, actual, expected):
+ for k, v in actual.items():
+ self.assertEqual(sorted(expected[k]), sorted(v))
+
+ def _apply_and_verify(self, apply_fn, config, expected_cfgs=None,
+ bringup=False):
+ if not expected_cfgs:
+ raise ValueError('expected_cfg must not be None')
+
+ tmpd = None
+ with mock.patch('cloudinit.net.networkd.available') as m_avail:
+ m_avail.return_value = True
+ with self.reRooted(tmpd) as tmpd:
+ apply_fn(config, bringup)
+
+ results = dir2dict(tmpd)
+ for cfgpath, expected in expected_cfgs.items():
+ actual = self.create_conf_dict(results[cfgpath].splitlines())
+ self.compare_dicts(actual, expected)
+ self.assertEqual(0o644, get_mode(cfgpath, tmpd))
+
+ def nwk_file_path(self, ifname):
+ return '/etc/systemd/network/10-cloud-init-%s.network' % ifname
+
+ def net_cfg_1(self, ifname):
+ ret = """\
+ [Match]
+ Name=%s
+ [Network]
+ DHCP=no
+ [Address]
+ Address=192.168.1.5/24
+ [Route]
+ Gateway=192.168.1.254""" % ifname
+ return ret
+
+ def net_cfg_2(self, ifname):
+ ret = """\
+ [Match]
+ Name=%s
+ [Network]
+ DHCP=ipv4""" % ifname
+ return ret
+
+ def test_photon_network_config_v1(self):
+ tmp = self.net_cfg_1('eth0').splitlines()
+ expected_eth0 = self.create_conf_dict(tmp)
+
+ tmp = self.net_cfg_2('eth1').splitlines()
+ expected_eth1 = self.create_conf_dict(tmp)
+
+ expected_cfgs = {
+ self.nwk_file_path('eth0'): expected_eth0,
+ self.nwk_file_path('eth1'): expected_eth1,
+ }
+
+ self._apply_and_verify(self.distro.apply_network_config,
+ V1_NET_CFG,
+ expected_cfgs.copy())
+
+ def test_photon_network_config_v2(self):
+ tmp = self.net_cfg_1('eth7').splitlines()
+ expected_eth7 = self.create_conf_dict(tmp)
+
+ tmp = self.net_cfg_2('eth9').splitlines()
+ expected_eth9 = self.create_conf_dict(tmp)
+
+ expected_cfgs = {
+ self.nwk_file_path('eth7'): expected_eth7,
+ self.nwk_file_path('eth9'): expected_eth9,
+ }
+
+ self._apply_and_verify(self.distro.apply_network_config,
+ V2_NET_CFG,
+ expected_cfgs.copy())
+
+
def get_mode(path, target=None):
return os.stat(subp.target_path(target, path)).st_mode & 0o777
diff --git a/tests/unittests/test_handler/test_handler_set_hostname.py b/tests/unittests/test_handler/test_handler_set_hostname.py
index 73641b70..32ca3b7e 100644
--- a/tests/unittests/test_handler/test_handler_set_hostname.py
+++ b/tests/unittests/test_handler/test_handler_set_hostname.py
@@ -120,6 +120,32 @@ class TestHostname(t_help.FilesystemMockingTestCase):
contents = util.load_file(distro.hostname_conf_fn)
self.assertEqual('blah', contents.strip())
+ @mock.patch('cloudinit.distros.Distro.uses_systemd', return_value=False)
+ def test_photon_hostname(self, m_uses_systemd):
+ cfg1 = {
+ 'hostname': 'photon',
+ 'prefer_fqdn_over_hostname': True,
+ 'fqdn': 'test1.vmware.com',
+ }
+ cfg2 = {
+ 'hostname': 'photon',
+ 'prefer_fqdn_over_hostname': False,
+ 'fqdn': 'test2.vmware.com',
+ }
+
+ ds = None
+ distro = self._fetch_distro('photon', cfg1)
+ paths = helpers.Paths({'cloud_dir': self.tmp})
+ cc = cloud.Cloud(ds, paths, {}, distro, None)
+ self.patchUtils(self.tmp)
+ for c in [cfg1, cfg2]:
+ cc_set_hostname.handle('cc_set_hostname', c, cc, LOG, [])
+ contents = util.load_file(distro.hostname_conf_fn, decode=True)
+ if c['prefer_fqdn_over_hostname']:
+ self.assertEqual(contents.strip(), c['fqdn'])
+ else:
+ self.assertEqual(contents.strip(), c['hostname'])
+
def test_multiple_calls_skips_unchanged_hostname(self):
"""Only new hostname or fqdn values will generate a hostname call."""
distro = self._fetch_distro('debian')
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index b72a62b8..b2ddbf99 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -5,7 +5,7 @@ from cloudinit import distros
from cloudinit.net import cmdline
from cloudinit.net import (
eni, interface_has_own_mac, natural_sort_key, netplan, network_state,
- renderers, sysconfig)
+ renderers, sysconfig, networkd)
from cloudinit.sources.helpers import openstack
from cloudinit import temp_utils
from cloudinit import subp
@@ -821,6 +821,28 @@ iface eth1 inet static
NETWORK_CONFIGS = {
'small': {
+ 'expected_networkd_eth99': textwrap.dedent("""\
+ [Match]
+ Name=eth99
+ MACAddress=c0:d6:9f:2c:e8:80
+ [Network]
+ DHCP=ipv4
+ Domains=wark.maas
+ DNS=1.2.3.4 5.6.7.8
+ [Route]
+ Gateway=65.61.151.37
+ Destination=0.0.0.0/0
+ Metric=10000
+ """).rstrip(' '),
+ 'expected_networkd_eth1': textwrap.dedent("""\
+ [Match]
+ Name=eth1
+ MACAddress=cf:d6:af:48:e8:80
+ [Network]
+ DHCP=no
+ Domains=wark.maas
+ DNS=1.2.3.4 5.6.7.8
+ """).rstrip(' '),
'expected_eni': textwrap.dedent("""\
auto lo
iface lo inet loopback
@@ -938,6 +960,12 @@ NETWORK_CONFIGS = {
"""),
},
'v4_and_v6': {
+ 'expected_networkd': textwrap.dedent("""\
+ [Match]
+ Name=iface0
+ [Network]
+ DHCP=yes
+ """).rstrip(' '),
'expected_eni': textwrap.dedent("""\
auto lo
iface lo inet loopback
@@ -973,6 +1001,17 @@ NETWORK_CONFIGS = {
""").rstrip(' '),
},
'v4_and_v6_static': {
+ 'expected_networkd': textwrap.dedent("""\
+ [Match]
+ Name=iface0
+ [Link]
+ MTUBytes=8999
+ [Network]
+ DHCP=no
+ [Address]
+ Address=192.168.14.2/24
+ Address=2001:1::1/64
+ """).rstrip(' '),
'expected_eni': textwrap.dedent("""\
auto lo
iface lo inet loopback
@@ -1059,6 +1098,12 @@ NETWORK_CONFIGS = {
""").rstrip(' '),
},
'dhcpv6_only': {
+ 'expected_networkd': textwrap.dedent("""\
+ [Match]
+ Name=iface0
+ [Network]
+ DHCP=ipv6
+ """).rstrip(' '),
'expected_eni': textwrap.dedent("""\
auto lo
iface lo inet loopback
@@ -4986,26 +5031,199 @@ class TestEniRoundTrip(CiTestCase):
files['/etc/network/interfaces'].splitlines())
+class TestNetworkdNetRendering(CiTestCase):
+
+ def create_conf_dict(self, contents):
+ content_dict = {}
+ for line in contents:
+ if line:
+ line = line.strip()
+ if line and re.search(r'^\[(.+)\]$', line):
+ content_dict[line] = []
+ key = line
+ elif line:
+ content_dict[key].append(line)
+
+ return content_dict
+
+ def compare_dicts(self, actual, expected):
+ for k, v in actual.items():
+ self.assertEqual(sorted(expected[k]), sorted(v))
+
+ @mock.patch("cloudinit.net.util.chownbyname", return_value=True)
+ @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_networkd_default_generation(self, mock_get_devicelist,
+ mock_read_sys_net,
+ mock_sys_dev_path,
+ m_get_cmdline,
+ m_chown):
+ 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)
+
+ render_target = 'etc/systemd/network/10-cloud-init-eth1000.network'
+ renderer = networkd.Renderer({})
+ renderer.render_network_state(ns, target=render_dir)
+
+ self.assertTrue(os.path.exists(os.path.join(render_dir,
+ render_target)))
+ with open(os.path.join(render_dir, render_target)) as fh:
+ contents = fh.readlines()
+
+ actual = self.create_conf_dict(contents)
+ print(actual)
+
+ expected = textwrap.dedent("""\
+ [Match]
+ Name=eth1000
+ MACAddress=07-1c-c6-75-a4-be
+ [Network]
+ DHCP=ipv4""").rstrip(' ')
+
+ expected = self.create_conf_dict(expected.splitlines())
+
+ self.compare_dicts(actual, expected)
+
+
+class TestNetworkdRoundTrip(CiTestCase):
+
+ def create_conf_dict(self, contents):
+ content_dict = {}
+ for line in contents:
+ if line:
+ line = line.strip()
+ if line and re.search(r'^\[(.+)\]$', line):
+ content_dict[line] = []
+ key = line
+ elif line:
+ content_dict[key].append(line)
+
+ return content_dict
+
+ def compare_dicts(self, actual, expected):
+ for k, v in actual.items():
+ self.assertEqual(sorted(expected[k]), sorted(v))
+
+ def _render_and_read(self, network_config=None, state=None, nwkd_path=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")
+
+ if not nwkd_path:
+ nwkd_path = '/etc/systemd/network/'
+
+ renderer = networkd.Renderer(config={'network_conf_dir': nwkd_path})
+
+ renderer.render_network_state(ns, target=dir)
+ return dir2dict(dir)
+
+ @mock.patch("cloudinit.net.util.chownbyname", return_value=True)
+ def testsimple_render_small_networkd(self, m_chown):
+ nwk_fn1 = '/etc/systemd/network/10-cloud-init-eth99.network'
+ nwk_fn2 = '/etc/systemd/network/10-cloud-init-eth1.network'
+ entry = NETWORK_CONFIGS['small']
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
+
+ actual = files[nwk_fn1].splitlines()
+ actual = self.create_conf_dict(actual)
+
+ expected = entry['expected_networkd_eth99'].splitlines()
+ expected = self.create_conf_dict(expected)
+
+ self.compare_dicts(actual, expected)
+
+ actual = files[nwk_fn2].splitlines()
+ actual = self.create_conf_dict(actual)
+
+ expected = entry['expected_networkd_eth1'].splitlines()
+ expected = self.create_conf_dict(expected)
+
+ self.compare_dicts(actual, expected)
+
+ @mock.patch("cloudinit.net.util.chownbyname", return_value=True)
+ def testsimple_render_v4_and_v6(self, m_chown):
+ nwk_fn = '/etc/systemd/network/10-cloud-init-iface0.network'
+ entry = NETWORK_CONFIGS['v4_and_v6']
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
+
+ actual = files[nwk_fn].splitlines()
+ actual = self.create_conf_dict(actual)
+
+ expected = entry['expected_networkd'].splitlines()
+ expected = self.create_conf_dict(expected)
+
+ self.compare_dicts(actual, expected)
+
+ @mock.patch("cloudinit.net.util.chownbyname", return_value=True)
+ def testsimple_render_v4_and_v6_static(self, m_chown):
+ nwk_fn = '/etc/systemd/network/10-cloud-init-iface0.network'
+ entry = NETWORK_CONFIGS['v4_and_v6_static']
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
+
+ actual = files[nwk_fn].splitlines()
+ actual = self.create_conf_dict(actual)
+
+ expected = entry['expected_networkd'].splitlines()
+ expected = self.create_conf_dict(expected)
+
+ self.compare_dicts(actual, expected)
+
+ @mock.patch("cloudinit.net.util.chownbyname", return_value=True)
+ def testsimple_render_dhcpv6_only(self, m_chown):
+ nwk_fn = '/etc/systemd/network/10-cloud-init-iface0.network'
+ entry = NETWORK_CONFIGS['dhcpv6_only']
+ files = self._render_and_read(network_config=yaml.load(entry['yaml']))
+
+ actual = files[nwk_fn].splitlines()
+ actual = self.create_conf_dict(actual)
+
+ expected = entry['expected_networkd'].splitlines()
+ expected = self.create_conf_dict(expected)
+
+ self.compare_dicts(actual, expected)
+
+
class TestRenderersSelect:
@pytest.mark.parametrize(
- 'renderer_selected,netplan,eni,nm,scfg,sys', (
+ 'renderer_selected,netplan,eni,nm,scfg,sys,networkd', (
# -netplan -ifupdown -nm -scfg -sys raises error
- (net.RendererNotFoundError, False, False, False, False, False),
+ (net.RendererNotFoundError, False, False, False, False, False,
+ False),
# -netplan +ifupdown -nm -scfg -sys selects eni
- ('eni', False, True, False, False, False),
+ ('eni', False, True, False, False, False, False),
# +netplan +ifupdown -nm -scfg -sys selects eni
- ('eni', True, True, False, False, False),
+ ('eni', True, True, False, False, False, False),
# +netplan -ifupdown -nm -scfg -sys selects netplan
- ('netplan', True, False, False, False, False),
+ ('netplan', True, False, False, False, False, False),
# Ubuntu with Network-Manager installed
# +netplan -ifupdown +nm -scfg -sys selects netplan
- ('netplan', True, False, True, False, False),
+ ('netplan', True, False, 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),
+ ('sysconfig', False, False, True, False, True, False),
+ # -netplan -ifupdown -nm -scfg -sys +networkd selects networkd
+ ('networkd', False, False, 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")
@@ -5013,7 +5231,8 @@ class TestRenderersSelect:
@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, renderer_selected, netplan, eni, nm, scfg, sys
+ 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
@@ -5021,6 +5240,7 @@ class TestRenderersSelect:
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
if isinstance(renderer_selected, str):
(renderer_name, _rnd_class) = renderers.select(
priority=renderers.DEFAULT_PRIORITY
@@ -5094,6 +5314,12 @@ class TestNetRenderers(CiTestCase):
result = sysconfig.available()
self.assertTrue(result)
+ @mock.patch("cloudinit.net.renderers.networkd.available")
+ def test_networkd_available(self, m_nwkd_avail):
+ m_nwkd_avail.return_value = True
+ found = renderers.search(priority=['networkd'], first=False)
+ self.assertEqual('networkd', found[0][0])
+
@mock.patch(
"cloudinit.net.is_openvswitch_internal_interface",
diff --git a/tests/unittests/test_render_cloudcfg.py b/tests/unittests/test_render_cloudcfg.py
index 495e2669..275879af 100644
--- a/tests/unittests/test_render_cloudcfg.py
+++ b/tests/unittests/test_render_cloudcfg.py
@@ -10,7 +10,8 @@ from cloudinit import util
# TODO(Look to align with tools.render-cloudcfg or cloudinit.distos.OSFAMILIES)
DISTRO_VARIANTS = ["amazon", "arch", "centos", "debian", "fedora", "freebsd",
- "netbsd", "openbsd", "rhel", "suse", "ubuntu", "unknown"]
+ "netbsd", "openbsd", "photon", "rhel", "suse", "ubuntu",
+ "unknown"]
@pytest.mark.allow_subp_for(sys.executable)