summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2022-08-30 13:29:28 -0600
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2022-08-30 21:29:10 +0000
commitbb2229803764a77f8b45a533f71f8e9742585bf2 (patch)
tree7aa5daa7dd9a45fef4489a9873368de154f1648a
parent611f032b07fa6fdd0e59b165a12d02201e61ba35 (diff)
downloadcloud-init-git-bb2229803764a77f8b45a533f71f8e9742585bf2.tar.gz
22.3-13-g70ce6442-0ubuntu1~22.10.1 (patches unapplied)
Imported using git-ubuntu import.
-rw-r--r--Makefile11
-rwxr-xr-xcloudinit/cmd/devel/net_convert.py12
-rw-r--r--cloudinit/config/cc_ubuntu_autoinstall.py11
-rw-r--r--cloudinit/config/cc_wireguard.py2
-rw-r--r--cloudinit/distros/__init__.py16
-rw-r--r--cloudinit/distros/arch.py5
-rw-r--r--cloudinit/distros/debian.py4
-rw-r--r--cloudinit/net/bsd.py9
-rw-r--r--cloudinit/net/eni.py9
-rw-r--r--cloudinit/net/ephemeral.py4
-rw-r--r--cloudinit/net/netplan.py9
-rw-r--r--cloudinit/net/network_manager.py13
-rw-r--r--cloudinit/net/network_state.py77
-rw-r--r--cloudinit/net/networkd.py29
-rw-r--r--cloudinit/net/renderer.py13
-rw-r--r--cloudinit/net/sysconfig.py9
-rw-r--r--debian/changelog18
-rw-r--r--doc/rtd/topics/modules.rst1
-rw-r--r--tests/integration_tests/modules/test_ansible.py15
-rw-r--r--tests/integration_tests/modules/test_combined.py2
-rw-r--r--tests/unittests/cmd/devel/test_net_convert.py42
-rw-r--r--tests/unittests/conftest.py10
-rw-r--r--tests/unittests/distros/test_netconfig.py51
-rw-r--r--tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.2653.nmconnection21
-rw-r--r--tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.nmconnection12
-rw-r--r--tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-en.nmconnection16
-rw-r--r--tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-eth.nmconnection16
-rw-r--r--tests/unittests/net/artifacts/no_matching_mac_v2.yaml22
-rw-r--r--tests/unittests/net/test_ephemeral.py49
-rw-r--r--tests/unittests/net/test_net_rendering.py101
-rw-r--r--tests/unittests/net/test_network_state.py3
-rw-r--r--tests/unittests/net/test_networkd.py9
-rw-r--r--tests/unittests/test_net.py21
-rw-r--r--tools/.github-cla-signers1
-rw-r--r--tox.ini3
35 files changed, 550 insertions, 96 deletions
diff --git a/Makefile b/Makefile
index 72faa04a..2acf132e 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,6 @@ YAML_FILES=$(shell find cloudinit tests tools -name "*.yaml" -type f )
YAML_FILES+=$(shell find doc/examples -name "cloud-config*.txt" -type f )
PYTHON = python3
-PIP_INSTALL := pip3 install
NUM_ITER ?= 100
@@ -56,14 +55,6 @@ ci-deps-ubuntu:
ci-deps-centos:
@$(PYTHON) $(CWD)/tools/read-dependencies --distro centos --test-distro
-pip-requirements:
- @echo "Installing cloud-init dependencies..."
- $(PIP_INSTALL) -r "$@.txt" -q
-
-pip-test-requirements:
- @echo "Installing cloud-init test dependencies..."
- $(PIP_INSTALL) -r "$@.txt" -q
-
test: unittest
check_version:
@@ -174,6 +165,6 @@ fix_spelling:
sh
.PHONY: all check test flake8 clean rpm srpm deb deb-src yaml
-.PHONY: check_version pip-test-requirements pip-requirements clean_pyc
+.PHONY: check_version clean_pyc
.PHONY: unittest style-check fix_spelling render-template benchmark-generator
.PHONY: clean_pytest clean_packaging check_spelling clean_release doc
diff --git a/cloudinit/cmd/devel/net_convert.py b/cloudinit/cmd/devel/net_convert.py
index 3e6cdd95..269d72cd 100755
--- a/cloudinit/cmd/devel/net_convert.py
+++ b/cloudinit/cmd/devel/net_convert.py
@@ -133,19 +133,14 @@ def handle_args(name, args):
config = ovf.Config(ovf.ConfigFile(args.network_data.name))
pre_ns = ovf.get_network_config_from_conf(config, False)
- ns = network_state.parse_net_config_data(pre_ns)
-
- if args.debug:
- sys.stderr.write("\n".join(["", "Internal State", yaml.dump(ns), ""]))
distro_cls = distros.fetch(args.distro)
distro = distro_cls(args.distro, {}, None)
- config = {}
if args.output_kind == "eni":
r_cls = eni.Renderer
config = distro.renderer_configs.get("eni")
elif args.output_kind == "netplan":
r_cls = netplan.Renderer
- config = distro.renderer_configs.get("netplan")
+ config = distro.renderer_configs.get("netplan", {})
# don't run netplan generate/apply
config["postcmds"] = False
# trim leading slash
@@ -165,6 +160,11 @@ def handle_args(name, args):
raise RuntimeError("Invalid output_kind")
r = r_cls(config=config)
+ ns = network_state.parse_net_config_data(pre_ns, renderer=r)
+
+ if args.debug:
+ sys.stderr.write("\n".join(["", "Internal State", yaml.dump(ns), ""]))
+
sys.stderr.write(
"".join(
[
diff --git a/cloudinit/config/cc_ubuntu_autoinstall.py b/cloudinit/config/cc_ubuntu_autoinstall.py
index a6180fe6..3d79c9ea 100644
--- a/cloudinit/config/cc_ubuntu_autoinstall.py
+++ b/cloudinit/config/cc_ubuntu_autoinstall.py
@@ -21,20 +21,21 @@ distros = ["ubuntu"]
meta: MetaSchema = {
"id": "cc_ubuntu_autoinstall",
- "name": "Autoinstall",
+ "name": "Ubuntu Autoinstall",
"title": "Support Ubuntu live-server install syntax",
"description": dedent(
"""\
- Ubuntu's autoinstall syntax supports single-system automated installs
- in either the live-server or live-desktop installers.
+ Ubuntu's autoinstall YAML supports single-system automated installs
+ in either the live-server install, via the ``subiquity`` snap, or the
+ next generation desktop installer, via `ubuntu-desktop-install` snap.
When "autoinstall" directives are provided in either
- #cloud-config user-data or ``/etc/cloud/cloud.cfg.d`` validate
+ ``#cloud-config`` user-data or ``/etc/cloud/cloud.cfg.d`` validate
minimal autoinstall schema adherance and emit a warning if the
live-installer is not present.
The live-installer will use autoinstall directives to seed answers to
configuration prompts during system install to allow for a
- "touchless" Ubuntu system install.
+ "touchless" or non-interactive Ubuntu system install.
For more details on Ubuntu's autoinstaller:
https://ubuntu.com/server/docs/install/autoinstall
diff --git a/cloudinit/config/cc_wireguard.py b/cloudinit/config/cc_wireguard.py
index 8cfbf6f1..366aff40 100644
--- a/cloudinit/config/cc_wireguard.py
+++ b/cloudinit/config/cc_wireguard.py
@@ -264,7 +264,7 @@ def handle(name: str, cfg: dict, cloud: Cloud, log, args: list):
wg_section = cfg["wireguard"]
else:
LOG.debug(
- "Skipping module named %s," " no 'wireguard' configuration found",
+ "Skipping module named %s, no 'wireguard' configuration found",
name,
)
return
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index 7aafaa78..4a468cf8 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -16,7 +16,7 @@ import stat
import string
import urllib.parse
from io import StringIO
-from typing import Any, Mapping, Optional, Type
+from typing import Any, Mapping, MutableMapping, Optional, Type
from cloudinit import importer
from cloudinit import log as logging
@@ -25,6 +25,7 @@ from cloudinit.distros.parsers import hosts
from cloudinit.features import ALLOW_EC2_MIRRORS_ON_NON_AWS_INSTANCE_TYPES
from cloudinit.net import activators, eni, network_state, renderers
from cloudinit.net.network_state import parse_net_config_data
+from cloudinit.net.renderer import Renderer
from .networking import LinuxNetworking, Networking
@@ -78,7 +79,7 @@ class Distro(persistence.CloudInitPickleMixin, metaclass=abc.ABCMeta):
tz_zone_dir = "/usr/share/zoneinfo"
default_owner = "root:root"
init_cmd = ["service"] # systemctl, service etc
- renderer_configs: Mapping[str, Mapping[str, Any]] = {}
+ renderer_configs: Mapping[str, MutableMapping[str, Any]] = {}
_preferred_ntp_clients = None
networking_cls: Type[Networking] = LinuxNetworking
# This is used by self.shutdown_command(), and can be overridden in
@@ -129,7 +130,7 @@ class Distro(persistence.CloudInitPickleMixin, metaclass=abc.ABCMeta):
except activators.NoActivatorException:
return None
- def _write_network_state(self, network_state):
+ def _get_renderer(self) -> Renderer:
priority = util.get_cfg_by_path(
self._cfg, ("network", "renderers"), None
)
@@ -139,6 +140,9 @@ class Distro(persistence.CloudInitPickleMixin, metaclass=abc.ABCMeta):
"Selected renderer '%s' from priority list: %s", name, priority
)
renderer = render_cls(config=self.renderer_configs.get(name))
+ return renderer
+
+ def _write_network_state(self, network_state, renderer: Renderer):
renderer.render_network_state(network_state)
def _find_tz_file(self, tz):
@@ -241,15 +245,17 @@ class Distro(persistence.CloudInitPickleMixin, metaclass=abc.ABCMeta):
"""
# This method is preferred to apply_network which only takes
# a much less complete network config format (interfaces(5)).
- network_state = parse_net_config_data(netconfig)
try:
- self._write_network_state(network_state)
+ renderer = self._get_renderer()
except NotImplementedError:
# backwards compat until all distros have apply_network_config
return self._apply_network_from_network_config(
netconfig, bring_up=bring_up
)
+ network_state = parse_net_config_data(netconfig, renderer=renderer)
+ self._write_network_state(network_state, renderer)
+
# Now try to bring them up
if bring_up:
LOG.debug("Bringing up newly configured network interfaces")
diff --git a/cloudinit/distros/arch.py b/cloudinit/distros/arch.py
index 0bdfef83..2d5cfbf6 100644
--- a/cloudinit/distros/arch.py
+++ b/cloudinit/distros/arch.py
@@ -11,6 +11,7 @@ from cloudinit import log as logging
from cloudinit import subp, util
from cloudinit.distros import net_util
from cloudinit.distros.parsers.hostname import HostnameConf
+from cloudinit.net.renderer import Renderer
from cloudinit.net.renderers import RendererNotFoundError
from cloudinit.settings import PER_INSTANCE
@@ -61,9 +62,9 @@ class Distro(distros.Distro):
self.update_package_sources()
self.package_command("", pkgs=pkglist)
- def _write_network_state(self, network_state):
+ def _get_renderer(self) -> Renderer:
try:
- super()._write_network_state(network_state)
+ return super()._get_renderer()
except RendererNotFoundError as e:
# Fall back to old _write_network
raise NotImplementedError from e
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index 6dc1ad40..87f4cc9f 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -137,9 +137,9 @@ class Distro(distros.Distro):
self.update_package_sources()
self.package_command("install", pkgs=pkglist)
- def _write_network_state(self, network_state):
+ def _write_network_state(self, *args, **kwargs):
_maybe_remove_legacy_eth0()
- return super()._write_network_state(network_state)
+ return super()._write_network_state(*args, **kwargs)
def _write_hostname(self, hostname, filename):
conf = None
diff --git a/cloudinit/net/bsd.py b/cloudinit/net/bsd.py
index ff5c7413..e0f18366 100644
--- a/cloudinit/net/bsd.py
+++ b/cloudinit/net/bsd.py
@@ -1,11 +1,13 @@
# This file is part of cloud-init. See LICENSE file for license information.
import re
+from typing import Optional
from cloudinit import log as logging
from cloudinit import net, subp, util
from cloudinit.distros import bsd_utils
from cloudinit.distros.parsers.resolv_conf import ResolvConf
+from cloudinit.net.network_state import NetworkState
from . import renderer
@@ -156,7 +158,12 @@ class BSDRenderer(renderer.Renderer):
0o644,
)
- def render_network_state(self, network_state, templates=None, target=None):
+ def render_network_state(
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
if target:
self.target = target
self._ifconfig_entries(settings=network_state)
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
index b0ec67bd..ea0b8e4a 100644
--- a/cloudinit/net/eni.py
+++ b/cloudinit/net/eni.py
@@ -4,10 +4,12 @@ import copy
import glob
import os
import re
+from typing import Optional
from cloudinit import log as logging
from cloudinit import subp, util
from cloudinit.net import subnet_is_ipv6
+from cloudinit.net.network_state import NetworkState
from . import ParserError, renderer
@@ -561,7 +563,12 @@ class Renderer(renderer.Renderer):
return "\n\n".join(["\n".join(s) for s in sections]) + "\n"
- def render_network_state(self, network_state, templates=None, target=None):
+ def render_network_state(
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
fpeni = subp.target_path(target, self.eni_path)
util.ensure_dir(os.path.dirname(fpeni))
header = self.eni_header if self.eni_header else ""
diff --git a/cloudinit/net/ephemeral.py b/cloudinit/net/ephemeral.py
index c0d83d29..81f7079f 100644
--- a/cloudinit/net/ephemeral.py
+++ b/cloudinit/net/ephemeral.py
@@ -429,9 +429,9 @@ class EphemeralIPNetwork:
# therefore catch exception unless only v4 is used
try:
if self.ipv4:
- self.stack.enter_context(EphemeralIPv6Network(self.interface))
- if self.ipv6:
self.stack.enter_context(EphemeralDHCPv4(self.interface))
+ if self.ipv6:
+ self.stack.enter_context(EphemeralIPv6Network(self.interface))
# v6 link local might be usable
# caller may want to log network state
except NoDHCPLeaseError as e:
diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
index d63d86d8..7b91077d 100644
--- a/cloudinit/net/netplan.py
+++ b/cloudinit/net/netplan.py
@@ -3,7 +3,7 @@
import copy
import os
import textwrap
-from typing import cast
+from typing import Optional, cast
from cloudinit import log as logging
from cloudinit import safeyaml, subp, util
@@ -240,7 +240,12 @@ class Renderer(renderer.Renderer):
LOG.debug("Failed to list features from netplan info: %s", e)
return self._features
- def render_network_state(self, network_state, templates=None, target=None):
+ def render_network_state(
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
# check network state for version
# if v2, then extract network_state.config
# else render_v2_from_state
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
index 8fd15575..8053511c 100644
--- a/cloudinit/net/network_manager.py
+++ b/cloudinit/net/network_manager.py
@@ -11,10 +11,12 @@ import io
import itertools
import os
import uuid
+from typing import Optional
from cloudinit import log as logging
from cloudinit import subp, util
from cloudinit.net import is_ipv6_address, subnet_is_ipv6
+from cloudinit.net.network_state import NetworkState
from . import renderer
@@ -69,7 +71,7 @@ class NMConnection:
method_map = {
"static": "manual",
- "dhcp6": "dhcp",
+ "dhcp6": "auto",
"ipv6_slaac": "auto",
"ipv6_dhcpv6-stateless": "auto",
"ipv6_dhcpv6-stateful": "auto",
@@ -96,8 +98,6 @@ class NMConnection:
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):
"""
@@ -344,7 +344,12 @@ class Renderer(renderer.Renderer):
# Well, what can we do...
return con_id
- def render_network_state(self, network_state, templates=None, target=None):
+ def render_network_state(
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
# First pass makes sure there's NMConnections for all known
# interfaces that have UUIDs that can be linked to from related
# interfaces
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
index 80f2b108..e4f7a7fd 100644
--- a/cloudinit/net/network_state.py
+++ b/cloudinit/net/network_state.py
@@ -7,7 +7,7 @@
import copy
import functools
import logging
-from typing import Any, Dict
+from typing import TYPE_CHECKING, Any, Dict, Optional
from cloudinit import safeyaml, util
from cloudinit.net import (
@@ -22,6 +22,9 @@ from cloudinit.net import (
net_prefix_to_ipv4_mask,
)
+if TYPE_CHECKING:
+ from cloudinit.net.renderer import Renderer
+
LOG = logging.getLogger(__name__)
NETWORK_STATE_VERSION = 1
@@ -136,14 +139,16 @@ class CommandHandlerMeta(type):
class NetworkState(object):
- def __init__(self, network_state, version=NETWORK_STATE_VERSION):
+ def __init__(
+ self, network_state: dict, version: int = NETWORK_STATE_VERSION
+ ):
self._network_state = copy.deepcopy(network_state)
self._version = version
self.use_ipv6 = network_state.get("use_ipv6", False)
self._has_default_route = None
@property
- def config(self):
+ def config(self) -> dict:
return self._network_state["config"]
@property
@@ -204,6 +209,20 @@ class NetworkState(object):
route.get("prefix") == 0 and route.get("network") in default_nets
)
+ @classmethod
+ def to_passthrough(cls, network_state: dict) -> "NetworkState":
+ """Instantiates a `NetworkState` without interpreting its data.
+
+ That means only `config` and `version` are copied.
+
+ :param network_state: Network state data.
+ :return: Instance of `NetworkState`.
+ """
+ kwargs = {}
+ if "version" in network_state:
+ kwargs["version"] = network_state["version"]
+ return cls({"config": network_state}, **kwargs)
+
class NetworkStateInterpreter(metaclass=CommandHandlerMeta):
@@ -218,16 +237,27 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta):
"config": None,
}
- def __init__(self, version=NETWORK_STATE_VERSION, config=None):
+ def __init__(
+ self,
+ version=NETWORK_STATE_VERSION,
+ config=None,
+ renderer=None, # type: Optional[Renderer]
+ ):
self._version = version
self._config = config
self._network_state = copy.deepcopy(self.initial_network_state)
self._network_state["config"] = config
self._parsed = False
- self._interface_dns_map = {}
+ self._interface_dns_map: dict = {}
+ self._renderer = renderer
@property
- def network_state(self):
+ def network_state(self) -> NetworkState:
+ from cloudinit.net.netplan import Renderer as NetplanRenderer
+
+ if self._version == 2 and isinstance(self._renderer, NetplanRenderer):
+ LOG.debug("Passthrough netplan v2 config")
+ return NetworkState.to_passthrough(self._config)
return NetworkState(self._network_state, version=self._version)
@property
@@ -268,10 +298,6 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta):
def as_dict(self):
return {"version": self._version, "config": self._config}
- def get_network_state(self):
- ns = self.network_state
- return ns
-
def parse_config(self, skip_broken=True):
if self._version == 1:
self.parse_config_v1(skip_broken=skip_broken)
@@ -316,6 +342,12 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta):
}
def parse_config_v2(self, skip_broken=True):
+ from cloudinit.net.netplan import Renderer as NetplanRenderer
+
+ if isinstance(self._renderer, NetplanRenderer):
+ # Nothing to parse as we are going to perform a Netplan passthrough
+ return
+
for command_type, command in self._config.items():
if command_type in ["version", "renderer"]:
continue
@@ -764,7 +796,7 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta):
" netplan rendering support."
)
- def _v2_common(self, cfg):
+ 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:
@@ -781,10 +813,13 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta):
name_cmd.update({"address": dns})
self.handle_nameserver(name_cmd)
- mac_address = dev_cfg.get("match", {}).get("macaddress")
- real_if_name = find_interface_name_from_mac(mac_address)
- if real_if_name:
- iface = real_if_name
+ 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)
@@ -1044,7 +1079,11 @@ def _normalize_subnets(subnets):
return [_normalize_subnet(s) for s in subnets]
-def parse_net_config_data(net_config, skip_broken=True) -> NetworkState:
+def parse_net_config_data(
+ net_config: dict,
+ skip_broken: bool = True,
+ renderer=None, # type: Optional[Renderer]
+) -> NetworkState:
"""Parses the config, returns NetworkState object
:param net_config: curtin network config dict
@@ -1058,9 +1097,11 @@ def parse_net_config_data(net_config, skip_broken=True) -> NetworkState:
config = net_config
if version and config is not None:
- nsi = NetworkStateInterpreter(version=version, config=config)
+ nsi = NetworkStateInterpreter(
+ version=version, config=config, renderer=renderer
+ )
nsi.parse_config(skip_broken=skip_broken)
- state = nsi.get_network_state()
+ state = nsi.network_state
if not state:
raise RuntimeError(
diff --git a/cloudinit/net/networkd.py b/cloudinit/net/networkd.py
index 7d7d82c2..e0a5d848 100644
--- a/cloudinit/net/networkd.py
+++ b/cloudinit/net/networkd.py
@@ -8,9 +8,11 @@
# This file is part of cloud-init. See LICENSE file for license information.
from collections import OrderedDict
+from typing import Optional
from cloudinit import log as logging
from cloudinit import subp, util
+from cloudinit.net.network_state import NetworkState
from . import renderer
@@ -44,10 +46,16 @@ class CfgParser:
for k, v in sorted(self.conf_dict.items()):
if not v:
continue
- contents += "[" + k + "]\n"
- for e in sorted(v):
- contents += e + "\n"
- contents += "\n"
+ if k == "Address":
+ for e in sorted(v):
+ contents += "[" + k + "]\n"
+ contents += e + "\n"
+ contents += "\n"
+ else:
+ contents += "[" + k + "]\n"
+ for e in sorted(v):
+ contents += e + "\n"
+ contents += "\n"
return contents
@@ -217,7 +225,12 @@ class Renderer(renderer.Renderer):
util.write_file(net_fn, conf)
util.chownbyname(net_fn, net_fn_owner, net_fn_owner)
- def render_network_state(self, network_state, templates=None, target=None):
+ def render_network_state(
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
network_dir = self.network_conf_dir
if target:
network_dir = subp.target_path(target) + network_dir
@@ -242,7 +255,7 @@ class Renderer(renderer.Renderer):
self.parse_routes(route, cfg)
if ns.version == 2:
- name = iface["name"]
+ name: Optional[str] = iface["name"]
# network state doesn't give dhcp domain info
# using ns.config as a workaround here
@@ -257,8 +270,8 @@ class Renderer(renderer.Renderer):
if dev_cfg.get("set-name") == name:
name = dev_name
break
-
- self.dhcp_domain(ns.config["ethernets"][name], cfg)
+ if name in ns.config["ethernets"]:
+ self.dhcp_domain(ns.config["ethernets"][name], cfg)
ret_dict.update({link: cfg.get_final_conf()})
diff --git a/cloudinit/net/renderer.py b/cloudinit/net/renderer.py
index da154731..d7bc19b1 100644
--- a/cloudinit/net/renderer.py
+++ b/cloudinit/net/renderer.py
@@ -7,6 +7,7 @@
import abc
import io
+from typing import Optional
from cloudinit.net.network_state import NetworkState, parse_net_config_data
from cloudinit.net.udev import generate_udev_rule
@@ -49,11 +50,19 @@ class Renderer(object):
return content.getvalue()
@abc.abstractmethod
- def render_network_state(self, network_state, templates=None, target=None):
+ def render_network_state(
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
"""Render network state."""
def render_network_config(
- self, network_config, templates=None, target=None
+ self,
+ network_config: dict,
+ templates: Optional[dict] = None,
+ target=None,
):
return self.render_network_state(
network_state=parse_net_config_data(network_config),
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 698724ab..d5789fb0 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -4,7 +4,7 @@ import copy
import io
import os
import re
-from typing import Mapping
+from typing import Mapping, Optional
from cloudinit import log as logging
from cloudinit import subp, util
@@ -980,8 +980,11 @@ class Renderer(renderer.Renderer):
return contents
def render_network_state(
- self, network_state: NetworkState, templates=None, target=None
- ):
+ self,
+ network_state: NetworkState,
+ templates: Optional[dict] = None,
+ target=None,
+ ) -> None:
if not templates:
templates = self.templates
file_mode = 0o644
diff --git a/debian/changelog b/debian/changelog
index 37334586..711fa812 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,21 @@
+cloud-init (22.3-13-g70ce6442-0ubuntu1~22.10.1) kinetic; urgency=medium
+
+ * New upstream snapshot.
+ + Fix v2 interface matching when no MAC (LP: #1986551)
+ + test: reduce number of network dependencies in flaky test (#1702)
+ + docs: publish cc_ubuntu_autoinstall docs to rtd (#1696)
+ + net: Fix EphemeraIPNetwork (#1697)
+ + test: make ansible test work across older versions (#1691)
+ + Networkd multi-address support/fix (#1685) [Teodor Garzdin]
+ + make: drop broken targets (#1688)
+ + net: Passthough v2 netconfigs in netplan systems (#1650)
+ (LP: #1978543)
+ + NM ipv6 connection does not work on Azure and Openstack (#1616)
+ [Emanuele Giuseppe Esposito]
+ + Fix check_format_tip (#1679)
+
+ -- Chad Smith <chad.smith@canonical.com> Tue, 30 Aug 2022 13:29:28 -0600
+
cloud-init (22.3-3-g9f0efc47-0ubuntu1~22.10.1) kinetic; urgency=medium
* New upstream snapshot.
diff --git a/doc/rtd/topics/modules.rst b/doc/rtd/topics/modules.rst
index 8ffb984d..cbe0f5d7 100644
--- a/doc/rtd/topics/modules.rst
+++ b/doc/rtd/topics/modules.rst
@@ -55,6 +55,7 @@ Module Reference
.. automodule:: cloudinit.config.cc_ssh_import_id
.. automodule:: cloudinit.config.cc_timezone
.. automodule:: cloudinit.config.cc_ubuntu_advantage
+.. automodule:: cloudinit.config.cc_ubuntu_autoinstall
.. automodule:: cloudinit.config.cc_ubuntu_drivers
.. automodule:: cloudinit.config.cc_update_etc_hosts
.. automodule:: cloudinit.config.cc_update_hostname
diff --git a/tests/integration_tests/modules/test_ansible.py b/tests/integration_tests/modules/test_ansible.py
index eebc7be9..0d979d40 100644
--- a/tests/integration_tests/modules/test_ansible.py
+++ b/tests/integration_tests/modules/test_ansible.py
@@ -31,8 +31,9 @@ write_files:
WantedBy=cloud-init-local.service
[Service]
- ExecStart=/usr/bin/env python3 -m http.server \
- --directory /root/playbooks/.git
+ WorkingDirectory=/root/playbooks/.git
+ ExecStart=/usr/bin/env python3 -m http.server --bind 0.0.0.0 8000
+
- path: /etc/systemd/system/repo_waiter.service
content: |
@@ -49,7 +50,7 @@ write_files:
# running and continue once it is up, but this is simple and works
[Service]
Type=oneshot
- ExecStart=sh -c "while \
+ ExecStart=/bin/sh -c "while \
! git clone http://0.0.0.0:8000/ $(mktemp -d); do sleep 0.1; done"
- path: /root/playbooks/ubuntu.yml
@@ -94,6 +95,9 @@ ansible:
"""
SETUP_REPO = f"cd {REPO_D} &&\
+git config --global user.name auto &&\
+git config --global user.email autom@tic.io &&\
+git config --global init.defaultBranch main &&\
git init {REPO_D} &&\
git add {REPO_D}/roles/apt/tasks/main.yml {REPO_D}/ubuntu.yml &&\
git commit -m auto &&\
@@ -101,8 +105,9 @@ git commit -m auto &&\
def _test_ansible_pull_from_local_server(my_client):
-
- assert my_client.execute(SETUP_REPO).ok
+ setup = my_client.execute(SETUP_REPO)
+ assert not setup.stderr
+ assert not setup.return_code
my_client.execute("cloud-init clean --logs")
my_client.restart()
log = my_client.read_from_file("/var/log/cloud-init.log")
diff --git a/tests/integration_tests/modules/test_combined.py b/tests/integration_tests/modules/test_combined.py
index 7e84626f..93523bfc 100644
--- a/tests/integration_tests/modules/test_combined.py
+++ b/tests/integration_tests/modules/test_combined.py
@@ -67,8 +67,8 @@ snap:
commands:
- snap install hello-world
ssh_import_id:
- - gh:powersj
- lp:smoser
+
timezone: US/Aleutian
"""
diff --git a/tests/unittests/cmd/devel/test_net_convert.py b/tests/unittests/cmd/devel/test_net_convert.py
index 60acb1a6..100aa8de 100644
--- a/tests/unittests/cmd/devel/test_net_convert.py
+++ b/tests/unittests/cmd/devel/test_net_convert.py
@@ -4,6 +4,7 @@ import itertools
import pytest
+from cloudinit import safeyaml as yaml
from cloudinit.cmd.devel import net_convert
from cloudinit.distros.debian import NETWORK_FILE_HEADER
from tests.unittests.helpers import mock
@@ -183,5 +184,46 @@ class TestNetConvert:
)
] == chown.call_args_list
+ @pytest.mark.parametrize("debug", (False, True))
+ def test_convert_netplan_passthrough(self, debug, tmpdir):
+ """Assert that if the network config's version is 2 and the renderer is
+ Netplan, then the config is passed through as-is.
+ """
+ network_data = tmpdir.join("network_data")
+ # `default` as a route supported by Netplan but not by cloud-init
+ content = """\
+ network:
+ version: 2
+ ethernets:
+ enp0s3:
+ dhcp4: false
+ addresses: [10.0.4.10/24]
+ nameservers:
+ addresses: [10.0.4.1]
+ routes:
+ - to: default
+ via: 10.0.4.1
+ metric: 100
+ """
+ network_data.write(content)
+ args = [
+ "-m",
+ "enp0s3,AA",
+ f"--directory={tmpdir.strpath}",
+ f"--network-data={network_data.strpath}",
+ "--distro=ubuntu",
+ "--kind=yaml",
+ "--output-kind=netplan",
+ ]
+ if debug:
+ args.append("--debug")
+ params = self._replace_path_args(args, tmpdir)
+ with mock.patch("sys.argv", ["net-convert"] + params):
+ args = net_convert.get_parser().parse_args()
+ with mock.patch("cloudinit.util.chownbyname"):
+ net_convert.handle_args("somename", args)
+ outfile = tmpdir.join("etc/netplan/50-cloud-init.yaml")
+ assert yaml.load(content) == yaml.load(outfile.read())
+
# vi: ts=4 expandtab
diff --git a/tests/unittests/conftest.py b/tests/unittests/conftest.py
index e265a285..1ab17e8b 100644
--- a/tests/unittests/conftest.py
+++ b/tests/unittests/conftest.py
@@ -1,6 +1,7 @@
import builtins
import glob
import os
+from pathlib import Path
import pytest
@@ -55,3 +56,12 @@ def fake_filesystem(mocker, tmpdir):
func = getattr(mod, f)
trap_func = retarget_many_wrapper(str(tmpdir), nargs, func)
mocker.patch.object(mod, f, trap_func)
+
+
+PYTEST_VERSION_TUPLE = tuple(map(int, pytest.__version__.split(".")))
+
+if PYTEST_VERSION_TUPLE < (3, 9, 0):
+
+ @pytest.fixture
+ def tmp_path(tmpdir):
+ return Path(tmpdir)
diff --git a/tests/unittests/distros/test_netconfig.py b/tests/unittests/distros/test_netconfig.py
index 38e92f0e..6509f1de 100644
--- a/tests/unittests/distros/test_netconfig.py
+++ b/tests/unittests/distros/test_netconfig.py
@@ -235,6 +235,38 @@ network:
"""
+V2_PASSTHROUGH_NET_CFG = {
+ "ethernets": {
+ "eth7": {
+ "addresses": ["192.168.1.5/24"],
+ "gateway4": "192.168.1.254",
+ "routes": [{"to": "default", "via": "10.0.4.1", "metric": 100}],
+ },
+ },
+ "version": 2,
+}
+
+
+V2_PASSTHROUGH_NET_CFG_OUTPUT = """\
+# This file is generated from information provided by the datasource. Changes
+# to it will not persist across an instance reboot. To disable cloud-init's
+# network configuration capabilities, write a file
+# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
+# network: {config: disabled}
+network:
+ ethernets:
+ eth7:
+ addresses:
+ - 192.168.1.5/24
+ gateway4: 192.168.1.254
+ routes:
+ - metric: 100
+ to: default
+ via: 10.0.4.1
+ version: 2
+"""
+
+
class WriteBuffer(object):
def __init__(self):
self.buffer = StringIO()
@@ -472,6 +504,9 @@ class TestNetCfgDistroUbuntuEni(TestNetCfgDistroBase):
class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase):
+
+ with_logs = True
+
def setUp(self):
super(TestNetCfgDistroUbuntuNetplan, self).setUp()
self.distro = self._get_distro("ubuntu", renderers=["netplan"])
@@ -540,6 +575,22 @@ class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase):
expected_cfgs=expected_cfgs.copy(),
)
+ def test_apply_network_config_v2_full_passthrough_ub(self):
+ expected_cfgs = {
+ self.netplan_path(): V2_PASSTHROUGH_NET_CFG_OUTPUT,
+ }
+ # ub_distro.apply_network_config(V2_PASSTHROUGH_NET_CFG, False)
+ self._apply_and_verify_netplan(
+ self.distro.apply_network_config,
+ V2_PASSTHROUGH_NET_CFG,
+ expected_cfgs=expected_cfgs.copy(),
+ )
+ self.assertIn("Passthrough netplan v2 config", self.logs.getvalue())
+ self.assertIn(
+ "Selected renderer 'netplan' from priority list: ['netplan']",
+ self.logs.getvalue(),
+ )
+
class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
def setUp(self):
diff --git a/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.2653.nmconnection b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.2653.nmconnection
new file mode 100644
index 00000000..80483d4f
--- /dev/null
+++ b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.2653.nmconnection
@@ -0,0 +1,21 @@
+# Generated by cloud-init. Changes will be lost.
+
+[connection]
+id=cloud-init encc000.2653
+uuid=116aaf19-aabc-50ea-b480-e9aee18bda59
+type=vlan
+interface-name=encc000.2653
+
+[user]
+org.freedesktop.NetworkManager.origin=cloud-init
+
+[vlan]
+id=2653
+parent=f869ebd3-f175-5747-bf02-d0d44d687248
+
+[ipv4]
+method=manual
+may-fail=false
+address1=10.245.236.14/24
+gateway=10.245.236.1
+dns=10.245.236.1;
diff --git a/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.nmconnection b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.nmconnection
new file mode 100644
index 00000000..3368388d
--- /dev/null
+++ b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-encc000.nmconnection
@@ -0,0 +1,12 @@
+# Generated by cloud-init. Changes will be lost.
+
+[connection]
+id=cloud-init encc000
+uuid=f869ebd3-f175-5747-bf02-d0d44d687248
+type=ethernet
+interface-name=encc000
+
+[user]
+org.freedesktop.NetworkManager.origin=cloud-init
+
+[ethernet]
diff --git a/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-en.nmconnection b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-en.nmconnection
new file mode 100644
index 00000000..16120bc1
--- /dev/null
+++ b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-en.nmconnection
@@ -0,0 +1,16 @@
+# Generated by cloud-init. Changes will be lost.
+
+[connection]
+id=cloud-init zz-all-en
+uuid=159daec9-cba3-5101-85e7-46d831857f43
+type=ethernet
+interface-name=zz-all-en
+
+[user]
+org.freedesktop.NetworkManager.origin=cloud-init
+
+[ethernet]
+
+[ipv4]
+method=auto
+may-fail=false
diff --git a/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-eth.nmconnection b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-eth.nmconnection
new file mode 100644
index 00000000..df44d546
--- /dev/null
+++ b/tests/unittests/net/artifacts/no_matching_mac/etc/NetworkManager/system-connections/cloud-init-zz-all-eth.nmconnection
@@ -0,0 +1,16 @@
+# Generated by cloud-init. Changes will be lost.
+
+[connection]
+id=cloud-init zz-all-eth
+uuid=23a83d8a-d7db-5133-a77b-e68a6ac61ec9
+type=ethernet
+interface-name=zz-all-eth
+
+[user]
+org.freedesktop.NetworkManager.origin=cloud-init
+
+[ethernet]
+
+[ipv4]
+method=auto
+may-fail=false
diff --git a/tests/unittests/net/artifacts/no_matching_mac_v2.yaml b/tests/unittests/net/artifacts/no_matching_mac_v2.yaml
new file mode 100644
index 00000000..f5fc5ef1
--- /dev/null
+++ b/tests/unittests/net/artifacts/no_matching_mac_v2.yaml
@@ -0,0 +1,22 @@
+network:
+ version: 2
+ ethernets:
+ encc000: {}
+ zz-all-en:
+ match:
+ name: "en*"
+ dhcp4: true
+ zz-all-eth:
+ match:
+ name: "eth*"
+ dhcp4: true
+ vlans:
+ encc000.2653:
+ id: 2653
+ link: "encc000"
+ addresses:
+ - "10.245.236.14/24"
+ gateway4: "10.245.236.1"
+ nameservers:
+ addresses:
+ - "10.245.236.1"
diff --git a/tests/unittests/net/test_ephemeral.py b/tests/unittests/net/test_ephemeral.py
new file mode 100644
index 00000000..d2237faf
--- /dev/null
+++ b/tests/unittests/net/test_ephemeral.py
@@ -0,0 +1,49 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+from unittest import mock
+
+import pytest
+
+from cloudinit.net.ephemeral import EphemeralIPNetwork
+
+M_PATH = "cloudinit.net.ephemeral."
+
+
+class TestEphemeralIPNetwork:
+ @pytest.mark.parametrize("ipv6", [False, True])
+ @pytest.mark.parametrize("ipv4", [False, True])
+ @mock.patch(M_PATH + "contextlib.ExitStack")
+ @mock.patch(M_PATH + "EphemeralIPv6Network")
+ @mock.patch(M_PATH + "EphemeralDHCPv4")
+ def test_stack_order(
+ self,
+ m_ephemeral_dhcp_v4,
+ m_ephemeral_ip_v6_network,
+ m_exit_stack,
+ ipv4,
+ ipv6,
+ ):
+ interface = object()
+ with EphemeralIPNetwork(interface, ipv4=ipv4, ipv6=ipv6):
+ pass
+ expected_call_args_list = []
+ if ipv4:
+ expected_call_args_list.append(
+ mock.call(m_ephemeral_dhcp_v4.return_value)
+ )
+ assert [mock.call(interface)] == m_ephemeral_dhcp_v4.call_args_list
+ else:
+ assert [] == m_ephemeral_dhcp_v4.call_args_list
+ if ipv6:
+ expected_call_args_list.append(
+ mock.call(m_ephemeral_ip_v6_network.return_value)
+ )
+ assert [
+ mock.call(interface)
+ ] == m_ephemeral_ip_v6_network.call_args_list
+ else:
+ assert [] == m_ephemeral_ip_v6_network.call_args_list
+ assert (
+ expected_call_args_list
+ == m_exit_stack.return_value.enter_context.call_args_list
+ )
diff --git a/tests/unittests/net/test_net_rendering.py b/tests/unittests/net/test_net_rendering.py
new file mode 100644
index 00000000..06feab89
--- /dev/null
+++ b/tests/unittests/net/test_net_rendering.py
@@ -0,0 +1,101 @@
+"""Home of the tests for end-to-end net rendering
+
+Tests defined here should take a v1 or v2 yaml config as input, and verify
+that the rendered network config is as expected. Input files are defined
+under `tests/unittests/net/artifacts` with the format of
+
+<test_name><format>.yaml
+
+For example, if my test name is "test_all_the_things" and I'm testing a
+v2 format, I should have a file named test_all_the_things_v2.yaml.
+
+If a renderer outputs multiple files, the expected files should live in
+the artifacts directory under the given test name. For example, if I'm
+expecting NetworkManager to output a file named eth0.nmconnection as
+part of my "test_all_the_things" test, then in the artifacts directory
+there should be a
+`test_all_the_things/etc/NetworkManager/system-connections/eth0.nmconnection`
+file.
+
+To add a new nominal test, create the input and output files, then add the test
+name to the `test_convert` test along with it's supported renderers.
+
+Before adding a test here, check that it is not already represented
+in `unittests/test_net.py`. While that file contains similar tests, it has
+become too large to be maintainable.
+"""
+import glob
+from enum import Flag, auto
+from pathlib import Path
+
+import pytest
+
+from cloudinit import safeyaml
+from cloudinit.net.netplan import Renderer as NetplanRenderer
+from cloudinit.net.network_manager import Renderer as NetworkManagerRenderer
+from cloudinit.net.network_state import NetworkState, parse_net_config_data
+
+ARTIFACT_DIR = Path(__file__).parent.absolute() / "artifacts"
+
+
+class Renderer(Flag):
+ Netplan = auto()
+ NetworkManager = auto()
+ Networkd = auto()
+
+
+@pytest.fixture(autouse=True)
+def setup(mocker):
+ mocker.patch("cloudinit.net.network_state.get_interfaces_by_mac")
+
+
+def _check_netplan(
+ network_state: NetworkState, netplan_path: Path, expected_config
+):
+ if network_state.version == 2:
+ renderer = NetplanRenderer(config={"netplan_path": netplan_path})
+ renderer.render_network_state(network_state)
+ assert safeyaml.load(netplan_path.read_text()) == expected_config, (
+ f"Netplan config generated at {netplan_path} does not match v2 "
+ "config defined for this test."
+ )
+ else:
+ raise NotImplementedError
+
+
+def _check_network_manager(network_state: NetworkState, tmp_path: Path):
+ renderer = NetworkManagerRenderer()
+ renderer.render_network_state(
+ network_state, target=str(tmp_path / "no_matching_mac")
+ )
+ expected_paths = glob.glob(
+ str(ARTIFACT_DIR / "no_matching_mac" / "**/*.nmconnection"),
+ recursive=True,
+ )
+ for expected_path in expected_paths:
+ expected_contents = Path(expected_path).read_text()
+ actual_path = tmp_path / expected_path.split(
+ str(ARTIFACT_DIR), maxsplit=1
+ )[1].lstrip("/")
+ assert (
+ actual_path.exists()
+ ), f"Expected {actual_path} to exist, but it does not"
+ actual_contents = actual_path.read_text()
+ assert expected_contents.strip() == actual_contents.strip()
+
+
+@pytest.mark.parametrize(
+ "test_name, renderers",
+ [("no_matching_mac_v2", Renderer.Netplan | Renderer.NetworkManager)],
+)
+def test_convert(test_name, renderers, tmp_path):
+ network_config = safeyaml.load(
+ Path(ARTIFACT_DIR, f"{test_name}.yaml").read_text()
+ )
+ network_state = parse_net_config_data(network_config["network"])
+ if Renderer.Netplan in renderers:
+ _check_netplan(
+ network_state, tmp_path / "netplan.yaml", network_config
+ )
+ if Renderer.NetworkManager in renderers:
+ _check_network_manager(network_state, tmp_path)
diff --git a/tests/unittests/net/test_network_state.py b/tests/unittests/net/test_network_state.py
index b76b5dd7..75d033dc 100644
--- a/tests/unittests/net/test_network_state.py
+++ b/tests/unittests/net/test_network_state.py
@@ -79,7 +79,8 @@ class TestNetworkStateParseConfig(CiTestCase):
ncfg = {"version": 2, "otherconfig": {}, "somemore": [1, 2, 3]}
network_state.parse_net_config_data(ncfg)
self.assertEqual(
- [mock.call(version=2, config=ncfg)], self.m_nsi.call_args_list
+ [mock.call(version=2, config=ncfg, renderer=None)],
+ self.m_nsi.call_args_list,
)
def test_valid_config_gets_network_state(self):
diff --git a/tests/unittests/net/test_networkd.py b/tests/unittests/net/test_networkd.py
index ee50e640..a22c5092 100644
--- a/tests/unittests/net/test_networkd.py
+++ b/tests/unittests/net/test_networkd.py
@@ -12,6 +12,7 @@ network:
eth0:
match:
macaddress: '00:11:22:33:44:55'
+ addresses: [172.16.10.2/12, 172.16.10.3/12]
nameservers:
search: [spam.local, eggs.local]
addresses: [8.8.8.8]
@@ -24,7 +25,13 @@ network:
addresses: [4.4.4.4]
"""
-V2_CONFIG_SET_NAME_RENDERED_ETH0 = """[Match]
+V2_CONFIG_SET_NAME_RENDERED_ETH0 = """[Address]
+Address=172.16.10.2/12
+
+[Address]
+Address=172.16.10.3/12
+
+[Match]
MACAddress=00:11:22:33:44:55
Name=eth0
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index bfc13734..525706d1 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -1248,9 +1248,8 @@ NETWORK_CONFIGS = {
may-fail=false
[ipv6]
- method=dhcp
+ method=auto
may-fail=false
- addr-gen-mode=stable-privacy
"""
),
@@ -1278,6 +1277,7 @@ NETWORK_CONFIGS = {
DHCP=no
[Address]
Address=192.168.14.2/24
+ [Address]
Address=2001:1::1/64
"""
).rstrip(" "),
@@ -1383,7 +1383,6 @@ NETWORK_CONFIGS = {
[ipv6]
method=manual
may-fail=false
- addr-gen-mode=stable-privacy
address1=2001:1::1/64
"""
@@ -1416,9 +1415,8 @@ NETWORK_CONFIGS = {
[ethernet]
[ipv6]
- method=dhcp
+ method=auto
may-fail=false
- addr-gen-mode=stable-privacy
[ipv4]
method=auto
@@ -1517,9 +1515,8 @@ NETWORK_CONFIGS = {
[ethernet]
[ipv6]
- method=dhcp
+ method=auto
may-fail=false
- addr-gen-mode=stable-privacy
"""
),
@@ -1750,7 +1747,6 @@ NETWORK_CONFIGS = {
[ipv6]
method=auto
may-fail=false
- addr-gen-mode=stable-privacy
"""
),
@@ -1862,7 +1858,6 @@ NETWORK_CONFIGS = {
[ipv6]
method=auto
may-fail=false
- addr-gen-mode=stable-privacy
"""
),
@@ -2683,7 +2678,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
[ipv6]
method=manual
may-fail=false
- addr-gen-mode=stable-privacy
address1=2001:1::1/64
route1=::/0,2001:4800:78ff:1b::1
@@ -2736,9 +2730,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
xmit_hash_policy=layer3+4
[ipv6]
- method=dhcp
+ method=auto
may-fail=false
- addr-gen-mode=stable-privacy
"""
),
@@ -3342,7 +3335,6 @@ iface bond0 inet6 static
[ipv6]
method=manual
may-fail=false
- addr-gen-mode=stable-privacy
address1=2001:1::1/92
route1=2001:67c::/32,2001:67c:1562::1
route2=3001:67c::/32,3001:67c:15::1
@@ -3463,7 +3455,6 @@ iface bond0 inet6 static
[ipv6]
method=manual
may-fail=false
- addr-gen-mode=stable-privacy
address1=2001:1::bbbb/96
route1=::/0,2001:1::1
@@ -3641,7 +3632,6 @@ iface bond0 inet6 static
[ipv6]
method=manual
may-fail=false
- addr-gen-mode=stable-privacy
address1=2001:1::100/96
"""
@@ -3666,7 +3656,6 @@ iface bond0 inet6 static
[ipv6]
method=manual
may-fail=false
- addr-gen-mode=stable-privacy
address1=2001:1::101/96
"""
diff --git a/tools/.github-cla-signers b/tools/.github-cla-signers
index 2a69fd57..271a4710 100644
--- a/tools/.github-cla-signers
+++ b/tools/.github-cla-signers
@@ -38,6 +38,7 @@ emmanuelthome
eslerm
esposem
GabrielNagy
+garzdin
giggsoff
hamalq
holmanb
diff --git a/tox.ini b/tox.ini
index 055b1f07..21527ff3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -99,6 +99,8 @@ commands =
deps =
black
flake8
+ hypothesis
+ hypothesis_jsonschema
isort
mypy
pylint
@@ -108,6 +110,7 @@ deps =
types-pyyaml
types-requests
types-setuptools
+ typing-extensions
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/integration-requirements.txt
commands =