From 850269646f5b4b30f3bf43f65cc0d244c09e0ed5 Mon Sep 17 00:00:00 2001
From: Lucas Ratusznei Fonseca <lucas.ratuszneifonseca@windriver.com>
Date: Wed, 26 Feb 2025 18:48:49 +0000
Subject: [PATCH] Revert "Convert apply_network_config.sh to Python and add
 automated tests"

This reverts commit 4d7a0438f626638c7a5b307ca32bfd2616f7661e.

Reason for revert: generated auto file in incorrect order, causing ifup to fail for some labels during startup.

Change-Id: Ic549a293bf28edf29f9d49d2a954791af4711f20
---
 .gitignore                                    |    1 -
 .zuul.yaml                                    |   15 -
 puppet-manifests/.stestr.conf                 |    3 -
 puppet-manifests/src/Makefile                 |    1 -
 .../src/bin/apply_network_config.py           | 1046 ------
 .../src/bin/apply_network_config.sh           |    2 -
 .../src/bin/k8s_wait_for_endpoints_health.py  |    9 +-
 puppet-manifests/src/bin/network_ifupdown.sh  |    2 -
 puppet-manifests/src/bin/network_sysconfig.sh |    2 -
 .../src/bin/puppet-update-grub-env.py         |   15 +-
 .../files/change_k8s_control_plane_params.py  |    8 +-
 .../src/modules/platform/manifests/network.pp |    4 +-
 puppet-manifests/test-requirements.txt        |    3 -
 puppet-manifests/tests/__init__.py            |    5 -
 puppet-manifests/tests/filesystem_mock.py     |  318 --
 .../tests/system_cmd_test_script.sh           |   36 -
 .../tests/test_apply_network_config.py        | 3062 -----------------
 puppet-manifests/tox.ini                      |   16 +-
 pylint.rc                                     |    4 +-
 test-requirements.txt                         |    2 -
 tox.ini                                       |   14 +-
 21 files changed, 18 insertions(+), 4550 deletions(-)
 delete mode 100644 puppet-manifests/.stestr.conf
 delete mode 100644 puppet-manifests/src/bin/apply_network_config.py
 delete mode 100644 puppet-manifests/test-requirements.txt
 delete mode 100644 puppet-manifests/tests/__init__.py
 delete mode 100644 puppet-manifests/tests/filesystem_mock.py
 delete mode 100755 puppet-manifests/tests/system_cmd_test_script.sh
 delete mode 100644 puppet-manifests/tests/test_apply_network_config.py

diff --git a/.gitignore b/.gitignore
index 81ccda05c..172bf5786 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
 .tox
-.stestr
diff --git a/.zuul.yaml b/.zuul.yaml
index 09ad56229..585abffbf 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -7,13 +7,11 @@
         - stx-puppet-linters
         - stx-puppet-tox-pep8
         - stx-puppet-tox-pylint
-        - puppet-manifests-tox-py39
     gate:
       jobs:
         - stx-puppet-linters
         - stx-puppet-tox-pep8
         - stx-puppet-tox-pylint
-        - puppet-manifests-tox-py39
     post:
       jobs:
         - stx-stx-puppet-upload-git-mirror
@@ -43,19 +41,6 @@
     vars:
       python_version: 3.9
 
-- job:
-    name: puppet-manifests-tox-py39
-    parent: openstack-tox-py39
-    description: |
-      Run py39 test for puppet-manifests
-    nodeset: debian-bullseye
-    files:
-      - puppet-manifests/*
-    vars:
-      tox_envlist: py39
-      python_version: 3.9
-      tox_extra_args: -c puppet-manifests/tox.ini
-
 - job:
     name: stx-stx-puppet-upload-git-mirror
     parent: upload-git-mirror
diff --git a/puppet-manifests/.stestr.conf b/puppet-manifests/.stestr.conf
deleted file mode 100644
index ea359caed..000000000
--- a/puppet-manifests/.stestr.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[DEFAULT]
-test_path=./tests
-top_dir=./
diff --git a/puppet-manifests/src/Makefile b/puppet-manifests/src/Makefile
index 2e3f3d818..44226e3e0 100644
--- a/puppet-manifests/src/Makefile
+++ b/puppet-manifests/src/Makefile
@@ -14,7 +14,6 @@ ifdef ignore_puppet_warnings
 else
 	install -m 755 -D bin/puppet-manifest-apply.sh $(BINDIR)/puppet-manifest-apply.sh
 endif
-	install -m 755 -D bin/apply_network_config.py $(BINDIR)/apply_network_config.py
 	install -m 755 -D bin/apply_network_config.sh $(BINDIR)/apply_network_config.sh
 	install -m 755 -D bin/k8s_wait_for_endpoints_health.py $(BINDIR)/k8s_wait_for_endpoints_health.py
 	install -m 755 -D bin/kube-wait-control-plane-terminated.sh $(BINDIR)/kube-wait-control-plane-terminated.sh
diff --git a/puppet-manifests/src/bin/apply_network_config.py b/puppet-manifests/src/bin/apply_network_config.py
deleted file mode 100644
index 9ef66ac3c..000000000
--- a/puppet-manifests/src/bin/apply_network_config.py
+++ /dev/null
@@ -1,1046 +0,0 @@
-#!/usr/bin/python3
-#
-# Copyright (c) 2025 Wind River Systems, Inc.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-import argparse
-from datetime import datetime
-import errno
-import fcntl
-import logging as LOG
-from netaddr import AddrFormatError
-from netaddr import IPAddress
-import os
-import re
-import signal
-import shlex
-import subprocess
-import sys
-import time
-
-LOG_FILE = "/var/log/user.log"
-PUPPET_DIR = "/var/run/network-scripts.puppet"
-PUPPET_FILE = "/var/run/network-scripts.puppet/interfaces"
-PUPPET_ROUTES_FILE = "/var/run/network-scripts.puppet/routes"
-PUPPET_ROUTES6_FILE = "/var/run/network-scripts.puppet/routes6"
-ETC_ROUTES_FILE = "/etc/network/routes"
-ETC_DIR = "/etc/network/interfaces.d"
-SYSINV_LOCK_FILE = "/var/run/apply_network_config.lock"
-UPGRADE_FILE = "/var/run/.network_upgrade_bootstrap"
-SUBCLOUD_ENROLLMENT_FILE = "/var/run/.enroll-init-reconfigure"
-CLOUD_INIT_FILE = ETC_DIR + "/50-cloud-init"
-IFSTATE_BASE_PATH = "/run/network/ifstate."
-DEVLINK_BASE_PATH = "/sys/class/net/"
-CFG_PREFIX = "ifcfg-"
-TERM_WAIT_TIME = 10
-
-# Interface types
-ETH = "eth"
-VLAN = "vlan"
-BONDING = "bonding"
-SLAVE = "slave"
-LABEL = "label"
-LO = "lo"
-
-# Order for setting interfaces down
-DOWN_ORDER = (LABEL, VLAN, BONDING, LO, ETH)
-# Order for setting interfaces up
-UP_ORDER = (ETH, LO, BONDING, VLAN, LABEL)
-# Order for configuring interfaces without down/up operation
-ONLINE_ORDER = (ETH, BONDING, VLAN, LABEL)
-
-# Interface property sort positions
-PROPERTY_SORT_POS = {
-    "iface": 0,
-    "vlan-raw-device": 1,
-    "address": 2,
-    "netmask": 3,
-    "gateway": 4,
-    "bond-master": 5,
-    "bond-miimon": 6,
-    "bond-mode": 7,
-    "bond-primary": 8,
-    "bond-slaves": 9,
-    "hwaddress": 10,
-    "mtu": 11,
-    "pre-up": 12,
-    "up": 13,
-    "post-up": 14,
-    "pre-down": 15,
-    "down": 16,
-    "post-down": 17,
-    "allow-": 19,
-    # Position DEFAULT_POS holds properties that are not in the list, allow- is put last to not
-    # break ifupdown parsing, see https://review.opendev.org/c/starlingx/stx-puppet/+/839620
-}
-
-# Default sort position for properties
-DEFAULT_POS = 18
-
-
-class InvalidNetmaskError(BaseException):
-    pass
-
-
-class StanzaParser():
-
-    @staticmethod
-    def ParseLines(lines):
-        parser = StanzaParser()
-        parser.parse_lines(lines)
-        return parser.get_auto_and_ifaces()
-
-    def __init__(self):
-        self.auto = []
-        self.auto_set = set()
-        self.ifaces = dict()
-        self.iface = None
-        self.state = "none"
-
-    def _proc_state_auto(self, verbs):
-        for iface in verbs[1:]:
-            if iface not in self.auto_set:
-                self.auto.append(iface)
-                self.auto_set.add(iface)
-        self.state = "none"
-
-    def _proc_state_start_iface(self, verbs):
-        self.iface = self.ifaces.setdefault(verbs[1], {verbs[0]: " ".join(verbs[1:])})
-        self.state = "continue-iface"
-
-    def _proc_state_continue_iface(self, verbs):
-        # Special case for allow- property
-        if "allow-" in verbs[0]:
-            self.iface["allow-"] = " ".join(verbs)
-        else:
-            self.iface[verbs[0]] = " ".join(verbs[1:]) if len(verbs) > 1 else None
-
-    STATES = {
-        "none": lambda self, line: None,
-        "start-auto": _proc_state_auto,
-        "start-iface": _proc_state_start_iface,
-        "continue-iface": _proc_state_continue_iface,
-        "standby-iface": lambda self, line: None,
-    }
-
-    NEXT_STATES = {
-        "none": {"new-auto": "start-auto",
-                 "new-iface": "start-iface"},
-        "continue-iface": {"new-auto": "start-auto",
-                           "new-iface": "start-iface",
-                           "empty": "standby-iface",
-                           "reset": "none"},
-        "standby-iface": {"new-auto": "start-auto",
-                          "new-iface": "start-iface",
-                          "continue": "continue-iface",
-                          "reset": "none"}
-    }
-
-    def _proc_state(self, verbs):
-        func = self.STATES[self.state]
-        func(self, verbs)
-
-    def _proc_event(self, event):
-        self.state = self.NEXT_STATES[self.state].get(event, self.state)
-
-    @staticmethod
-    def _get_event(verbs):
-        if len(verbs) == 0 or verbs[0].startswith("#"):
-            return "empty"
-        if verbs[0] == "auto":
-            return "new-auto"
-        if verbs[0] == "iface":
-            if len(verbs) > 1:
-                return "new-iface"
-            return "reset"
-        return "continue"
-
-    def _parse_line(self, line):
-        verbs = line.split()
-        event = self._get_event(verbs)
-        self._proc_event(event)
-        self._proc_state(verbs)
-
-    def parse_lines(self, lines):
-        for line in lines:
-            self._parse_line(line.strip())
-        self.state = "none"
-
-    def get_auto_and_ifaces(self):
-        return self.auto, self.ifaces
-
-
-def read_file_lines(path):
-    with open(path, "r") as f:
-        lines = f.readlines()
-    return [line.strip() for line in lines]
-
-
-def read_file_text(path):
-    with open(path, "r") as f:
-        return f.read()
-
-
-def is_label(iface):
-    return ":" in iface
-
-
-def get_base_iface(iface):
-    return iface.split(":")[0]
-
-
-def execute_system_cmd(cmd, timeout=30):
-    # When transitioning management network to a VLAN, ifup (for the mgmt interface) does its job
-    # in configuring the link but blocks sub.communicate() for a long period of time, long enough
-    # to cause the puppet task to end by timeout.
-    # If the subprocess is ended via sub.terminate(), sub.communicate() still blocks for an
-    # indefinite period of time. The only way that was found for the function to work as intended
-    # was to add start_new_session=True to subprocess.Popen() and to terminate the process group via
-    # os.killpg().
-
-    sub = subprocess.Popen(shlex.split(cmd),
-                           start_new_session=True,
-                           stdout=subprocess.PIPE,
-                           stderr=subprocess.STDOUT)
-    try:
-        stdout, _ = sub.communicate(timeout=timeout)
-        decoded_stdout = stdout.decode('utf-8')
-    except subprocess.TimeoutExpired:
-        pgid = os.getpgid(sub.pid)
-        LOG.warning(f"Execution time exceeded for command '{cmd}', "
-                    f"sending SIGTERM to subprocess (pid={sub.pid}, pgid={pgid})")
-        os.killpg(pgid, signal.SIGTERM)
-        try:
-            stdout, _ = sub.communicate(timeout=TERM_WAIT_TIME)
-        except subprocess.TimeoutExpired:
-            LOG.warning(f"Command '{cmd}' has not terminated after {TERM_WAIT_TIME} seconds, "
-                        f"sending SIGKILL to subprocess (pid={sub.pid}, pgid={pgid})")
-            os.killpg(pgid, signal.SIGKILL)
-            stdout, _ = sub.communicate()
-        decoded_stdout = stdout.decode('utf-8')
-        if sub.returncode == 0:
-            LOG.info(f"Command '{cmd}' output:{format_stdout(decoded_stdout)}")
-    return sub.returncode, decoded_stdout
-
-
-def apply_config(routes_only):
-    if routes_only:
-        LOG.info("Process Debian route config")
-        update_routes()
-    else:
-        if not os.path.isdir(PUPPET_DIR):
-            LOG.error("No puppet files? Nothing to do! Aborting...")
-            sys.exit(1)
-        LOG.info("Process Debian network config")
-        log_network_info()
-        updated_ifaces = update_interfaces()
-        update_routes(updated_ifaces)
-        check_enrollment_config()
-        log_network_info()
-    LOG.info("Finished")
-
-
-def log_network_info():
-    _, links = execute_system_cmd("/usr/sbin/ip addr show")
-    _, routes_ipv4 = execute_system_cmd("/usr/sbin/ip route show")
-    _, routes_ipv6 = execute_system_cmd("/usr/sbin/ip -6 route show")
-    LOG.info("Network info:\n************ Links/addresses ************\n"
-             f"{links}"
-             "************ IPv4 routes ****************\n"
-             f"{routes_ipv4}"
-             "************ IPv6 routes ****************\n"
-             f"{routes_ipv6}"
-             "*****************************************")
-
-
-def get_new_config():
-    '''Gets new network config from puppet directory'''
-    auto, ifaces = parse_interface_stanzas()
-    return build_config(auto, ifaces, is_from_puppet=True)
-
-
-def parse_interface_stanzas():
-    lines = read_file_lines(PUPPET_FILE)
-    return StanzaParser.ParseLines(lines)
-
-
-def get_current_config():
-    '''Gets current network config in etc directory'''
-    auto = parse_auto_file()
-    ifaces = parse_ifcfg_files(auto)
-    return build_config(auto, ifaces, is_from_puppet=False)
-
-
-def parse_auto_list(input_auto, ifaces, is_from_puppet):
-    valid_auto = []
-    invalid_auto = []
-    for iface in input_auto:
-        if iface in ifaces:
-            valid_auto.append(iface)
-        else:
-            invalid_auto.append(iface)
-    if invalid_auto:
-        origin = "PUPPET" if is_from_puppet else "ETC DIR"
-        LOG.error(f"Auto list from {origin} has interfaces that have no or invalid "
-                  f"config: {', '.join(invalid_auto)}")
-    return valid_auto
-
-
-def build_config(auto, ifaces, is_from_puppet):
-    valid_auto = parse_auto_list(auto, ifaces, is_from_puppet)
-    ifaces_types, dependencies = get_types_and_dependencies(ifaces)
-    return {"auto": set(valid_auto),
-            "ifaces": ifaces,
-            "ifaces_types": ifaces_types,
-            "dependencies": dependencies}
-
-
-def parse_auto_file():
-    path = get_auto_path()
-    if not os.path.isfile(path):
-        LOG.info(f"Auto file not found: '{path}'")
-        return []
-    lines = read_file_lines(path)
-    auto, _ = StanzaParser.ParseLines(lines)
-    return auto
-
-
-def get_auto_path():
-    return os.path.join(ETC_DIR, "auto")
-
-
-def get_ifcfg_path(iface):
-    return os.path.join(ETC_DIR, CFG_PREFIX + iface)
-
-
-def parse_ifcfg_files(ifaces):
-    iface_configs = dict()
-    for iface in ifaces:
-        iface_configs[iface] = parse_ifcfg_file(iface)
-    return iface_configs
-
-
-def parse_ifcfg_file(iface):
-    path = get_ifcfg_path(iface)
-    if not os.path.isfile(path):
-        LOG.warning(f"Interface config file not found: '{path}'")
-        return dict()
-    lines = read_file_lines(path)
-    _, ifaces = StanzaParser.ParseLines(lines)
-    if len(ifaces) == 0:
-        LOG.warning(f"No interface config found in '{path}'")
-        return dict()
-    if (ifconfig := ifaces.get(iface, None)) is None:
-        LOG.warning(f"Config for interface '{iface}' not found in '{path}'. Instead, file has "
-                    f"config(s) for the following interface(s): {' '.join(sorted(ifaces.keys()))}")
-        return dict()
-    if len(ifaces) > 1:
-        LOG.warning(f"Multiple interface configs found in '{path}': "
-                    f"{' '.join(sorted(ifaces.keys()))}")
-    return ifconfig
-
-
-def get_types_and_dependencies(iface_configs):
-    ifaces_types = dict()
-    dependencies = dict()
-
-    def set_type(iface, iftype):
-        ifaces_types[iface] = iftype
-
-    def add_dependent(iface, dependent):
-        entry = dependencies.setdefault(iface, set())
-        entry.add(dependent)
-
-    for iface, config in iface_configs.items():
-        if is_label(iface):
-            set_type(iface, LABEL)
-            parent = get_base_iface(iface)
-            add_dependent(parent, iface)
-        elif iface == "lo":
-            set_type(iface, LO)
-        elif vlan_attribs := get_vlan_attributes(iface, config):
-            set_type(iface, VLAN)
-            add_dependent(vlan_attribs[0], iface)
-        elif slaves := config.get("bond-slaves", None):
-            set_type(iface, BONDING)
-            for slave in slaves.split():
-                add_dependent(slave, iface)
-        elif master := config.get("bond-master", None):
-            set_type(iface, SLAVE)
-            add_dependent(iface, master)
-        else:
-            set_type(iface, ETH)
-
-    return ifaces_types, dependencies
-
-
-def get_vlan_attributes(iface, config):
-    '''Returns (vlan-raw-device, vlan-id) if iface is VLAN, else None'''
-    if result := re.search(R"^vlan([0-9]+)$", iface):
-        if raw_dev := config.get("vlan-raw-device", None):
-            return raw_dev, int(result.group(1))
-        LOG.warning("vlan-raw-device property is empty or not specified for "
-                    f"interface {iface}, so it will not be considered as a valid VLAN")
-        return None
-    if result := re.search(R"^(.*)\.([0-9]+)$", iface):
-        return result.group(1), int(result.group(2))
-    if preup := config.get("pre-up", None):
-        if result := re.search(R"ip\s+link\s+add\s+link\s+(\S+)\s+name\s+\S+\s+type"
-                               R"\s+vlan\s+id\s+(\d+)", preup):
-            return result.group(1), int(result.group(2))
-    return None
-
-
-def compare_configs(new_config, current_config):
-    added = new_config["auto"].difference(current_config["auto"])
-    if added:
-        LOG.info(f"Added interfaces: {' '.join(sorted(added))}")
-    removed = current_config["auto"].difference(new_config["auto"])
-    if removed:
-        LOG.info(f"Removed interfaces: {' '.join(sorted(removed))}")
-    modified = get_modified_ifaces(new_config, current_config)
-    if modified:
-        LOG.info(f"Modified interfaces: {' '.join(sorted(modified))}")
-    return {"added": added, "removed": removed, "modified": modified}
-
-
-def get_modified_ifaces(new_config, current_config):
-    modified = set()
-    new_ifaces = new_config["ifaces"]
-    current_ifaces = current_config["ifaces"]
-    for iface, new_if_config in new_ifaces.items():
-        current_if_config = current_ifaces.get(iface, None)
-        if not current_if_config:
-            continue
-        if is_iface_modified(iface, new_if_config, current_if_config):
-            modified.add(iface)
-    return modified
-
-
-def is_iface_modified(iface, new, current):
-    filtered_new = {p for p in new.keys() if p in PROPERTY_SORT_POS}
-    filtered_current = {p for p in current.keys() if p in PROPERTY_SORT_POS}
-    removed_props = filtered_current.difference(filtered_new)
-    added_props = filtered_new.difference(filtered_current)
-    modified_props = [p for p in filtered_new.intersection(filtered_current)
-                      if new[p] != current[p]]
-    if not removed_props and not added_props and not modified_props:
-        return False
-    text = f"Differences found for interface {iface}:"
-    if removed_props:
-        text += "\n    Removed properties:"
-        for prop in sort_properties(list(removed_props)):
-            text += f"\n        {prop} {current[prop]}"
-    if added_props:
-        text += "\n    Added properties:"
-        for prop in sort_properties(list(added_props)):
-            text += f"\n        {prop} {new[prop]}"
-    if modified_props:
-        text += "\n    Modified properties:"
-        for prop in sort_properties(list(modified_props)):
-            text += f"\n        '{prop}' went from '{current[prop]}' to '{new[prop]}'"
-    LOG.info(text)
-    return True
-
-
-def get_dependent_list(config, ifaces):
-    auto = config["auto"]
-    dep_map = config["dependencies"]
-    covered = set()
-
-    def add_dependent(iface):
-        if iface in covered or iface not in auto:
-            return
-        covered.add(iface)
-        dependents = dep_map.get(iface, None)
-        if not dependents:
-            return
-        for dependent in dependents:
-            add_dependent(dependent)
-
-    for iface in ifaces:
-        add_dependent(iface)
-
-    return covered
-
-
-def get_down_list(current_config, comparison):
-    base_set = comparison["modified"].union(comparison["removed"])
-    dependents = get_dependent_list(current_config, base_set)
-    return base_set.union(dependents)
-
-
-def get_up_list(new_config, comparison):
-    base_set = comparison["modified"].union(comparison["added"])
-    missing_set = get_missing_list(new_config, base_set)
-    up_set = base_set.union(missing_set)
-    dependents = get_dependent_list(new_config, up_set)
-    return up_set.union(dependents)
-
-
-def get_missing_list(config, base_set):
-    ifaces_types = config["ifaces_types"]
-    types = {ETH, BONDING, VLAN}
-    ifaces = {i for i in config["auto"].difference(base_set) if ifaces_types[i] in types}
-    out_set = set()
-    for iface in ifaces:
-        if is_iface_missing_or_down(iface):
-            LOG.info(f"Interface {iface} is missing or down, adding to up list")
-            out_set.add(iface)
-    return out_set
-
-
-def get_updated_ifaces(new_config, up_list):
-    ifaces_types = new_config["ifaces_types"]
-    types = {ETH, VLAN, BONDING, LO}
-    updated = set()
-    for iface in up_list:
-        if ifaces_types[iface] == LABEL:
-            updated.add(get_base_iface(iface))
-        elif ifaces_types[iface] in types:
-            updated.add(iface)
-    return updated
-
-
-def sort_ifaces_by_type(config, ifaces, type_order):
-    ifaces_types = config["ifaces_types"]
-    ifaces_by_type = dict()
-    for iface in ifaces:
-        iftype = ifaces_types[iface]
-        iface_list = ifaces_by_type.setdefault(iftype, [])
-        iface_list.append(iface)
-    sorted_ifaces = []
-    for iftype in type_order:
-        if iface_list := ifaces_by_type.get(iftype, None):
-            iface_list.sort()
-            sorted_ifaces.extend(iface_list)
-    return sorted_ifaces
-
-
-def set_ifaces_down(config, ifaces):
-    sorted_ifaces = sort_ifaces_by_type(config, ifaces, DOWN_ORDER)
-    for iface in sorted_ifaces:
-        set_iface_down(iface)
-
-
-def format_stdout(stdout):
-    cln_stdout = stdout.strip()
-    return f"\n{cln_stdout}" if "\n" in cln_stdout else f" '{cln_stdout}'"
-
-
-def set_iface_down(iface):
-    LOG.info(f"Bringing {iface} down")
-
-    ifstate_path = IFSTATE_BASE_PATH + iface
-    if os.path.isfile(ifstate_path) and read_file_text(ifstate_path).strip() == iface:
-        retcode, stdout = execute_system_cmd(f"/sbin/ifdown -v {iface}")
-        if retcode != 0:
-            LOG.error(f"Command 'ifdown' failed for interface {iface}:{format_stdout(stdout)}")
-
-    if not is_label(iface):
-        devlink_path = DEVLINK_BASE_PATH + iface
-        if os.path.islink(devlink_path):
-            retcode, stdout = execute_system_cmd(f"/usr/sbin/ip link set down dev {iface}")
-            if retcode != 0:
-                LOG.error(f"Command 'ip link set down' failed for "
-                          f"interface {iface}:{format_stdout(stdout)}")
-            retcode, stdout = execute_system_cmd(f"/usr/sbin/ip addr flush dev {iface}")
-            if retcode != 0:
-                LOG.error(f"Command 'ip addr flush' failed for interface {iface}:"
-                          f"{format_stdout(stdout)}")
-
-
-def set_ifaces_up(config, ifaces):
-    sorted_ifaces = sort_ifaces_by_type(config, ifaces, UP_ORDER)
-    for iface in sorted_ifaces:
-        set_iface_up(iface)
-
-
-def set_iface_up(iface):
-    LOG.info(f"Bringing {iface} up")
-    retcode, stdout = execute_system_cmd(f"/sbin/ifup -v {iface}")
-    if retcode != 0:
-        LOG.error(f"Command 'ifup' failed for interface {iface}: {format_stdout(stdout)}")
-    return retcode
-
-
-def update_files(new_config):
-    for iface, iface_config in new_config["ifaces"].items():
-        write_iface_config_file(iface, iface_config)
-    write_auto_file(new_config["auto"])
-
-
-def remove_iface_config_files(comparison):
-    for to_remove in comparison["removed"]:
-        remove_iface_config_file(to_remove)
-
-
-def path_exists(path):
-    return os.path.exists(path)
-
-
-def remove_iface_config_file(iface):
-    path = get_ifcfg_path(iface)
-    if path_exists(path):
-        LOG.info(f"Removing {path}")
-        try:
-            os.remove(path)
-        except OSError as e:
-            LOG.error(f"Failed to remove {path}: {e}")
-    else:
-        LOG.info(f"File {path} does not exist, no need to remove")
-
-
-def write_iface_config_file(iface, iface_config):
-    lines = get_ifcfg_lines(iface_config)
-    path = get_ifcfg_path(iface)
-    with open(path, "w") as f:
-        f.write("\n".join(lines) + "\n")
-
-
-def write_auto_file(auto):
-    contents = get_header() + "\nauto " + " ".join(auto) + "\n"
-    path = get_auto_path()
-    with open(path, "w") as f:
-        f.write(contents)
-
-
-def sort_properties(props):
-    # Key is position number (from PROPERTY_SORT_POS) with 2 digits followed by property name
-    def get_sort_key(v):
-        return f"{PROPERTY_SORT_POS.get(v, DEFAULT_POS):02d}{v}"
-    props.sort(key=get_sort_key)
-    return props
-
-
-def get_ifcfg_lines(iface_config):
-    props = list(iface_config.keys())
-    sort_properties(props)
-    lines = [get_header()]
-    for prop in props:
-        lines.append(iface_config[prop] if prop == "allow-" else prop + " " + iface_config[prop])
-    return lines
-
-
-def get_header():
-    dt = datetime.now().astimezone()
-    return dt.strftime("# HEADER: Last generated at: %Y-%m-%d %H:%M:%S %z")
-
-
-def get_route_entries(files):
-    entries = []
-    for file in files:
-        if os.path.isfile(file):
-            lines = read_file_lines(file)
-            entries.extend(get_route_entries_from_lines(lines, file))
-    return entries
-
-
-def get_route_entries_from_lines(lines, file):
-    routes = []
-    for line in lines:
-        clean_line = line.strip()
-        if len(clean_line) > 0 and not clean_line.startswith("#"):
-            verbs = clean_line.split()
-            if len(verbs) >= 4:
-                routes.append(' '.join(verbs))
-            else:
-                LOG.warning(f"Invalid route in file '{file}', must have at least 4 "
-                            f"parameters, {len(verbs)} found: '{clean_line}'")
-    return routes
-
-
-def get_route_iface(route):
-    return route.split()[3]
-
-
-def create_route_obj_from_entry(route_entry):
-    verbs = route_entry.split()
-    route_obj = {"network": verbs[0],
-                 "netmask": verbs[1],
-                 "nexthop": verbs[2],
-                 "ifname": verbs[3]}
-    if len(verbs) > 4:
-        route_obj["metric"] = verbs[5]
-    return route_obj
-
-
-def get_prefix_length(netmask):
-    try:
-        addr = IPAddress(netmask)
-        if addr.is_netmask():
-            return addr.netmask_bits()
-    except AddrFormatError:
-        pass
-    raise InvalidNetmaskError(f"Failed to get prefix length, invalid netmask: '{netmask}'")
-
-
-def get_linux_network(route):
-    network = route["network"]
-    if network == "default":
-        return "default"
-    prefixlen = get_prefix_length(route["netmask"])
-    return f"{network}/{prefixlen}"
-
-
-def remove_route_entry_from_kernel(route_entry):
-    route = create_route_obj_from_entry(route_entry)
-    try:
-        remove_route_from_kernel(route)
-    except InvalidNetmaskError as e:
-        LOG.error(f"Failed to remove route entry '{route_entry}' from the kernel: {e}")
-
-
-def remove_route_from_kernel(route):
-    description = get_route_description(route)
-    LOG.info(f"Removing route: {description}")
-    retcode, stdout = execute_system_cmd(f"/usr/sbin/ip route del {description}")
-    if retcode != 0:
-        LOG.error(f"Failed removing route {description}:{format_stdout(stdout)}")
-
-
-def add_route_entry_to_kernel(route_entry):
-    route = create_route_obj_from_entry(route_entry)
-    try:
-        add_route_to_kernel(route)
-    except InvalidNetmaskError as e:
-        LOG.error(f"Failed to add route entry '{route_entry}' to the kernel: {e}")
-
-
-def get_route_description(route, full=True):
-    linux_network = get_linux_network(route)
-    gateway = f" via {route['nexthop']} dev {route['ifname']}" if full else ""
-    descr = f"{linux_network}{gateway}"
-    if metric := route.get("metric", None):
-        descr += f" metric {metric}"
-    return descr
-
-
-def add_route_to_kernel(route):
-    prot = "-6 " if ":" in route["nexthop"] else ""
-    description = get_route_description(route)
-    LOG.info(f"Adding route: {description}")
-    retcode, stdout = execute_system_cmd(f"/usr/sbin/ip {prot}route show {description}")
-    if retcode == 0 and route["network"] in stdout:
-        LOG.info("Route already exists, skipping")
-    else:
-        short_descr = get_route_description(route, full=False)
-        retcode, stdout = execute_system_cmd(f"/usr/sbin/ip {prot}route show {short_descr}")
-        if retcode == 0 and route["network"] in stdout:
-            LOG.info(f"Route to specified network already exists, replacing: {stdout.strip()}")
-            retcode, stdout = execute_system_cmd(f"/usr/sbin/ip route replace {description}")
-            if retcode != 0:
-                LOG.error(f"Failed replacing route {description}:{format_stdout(stdout)}")
-        else:
-            retcode, stdout = execute_system_cmd(f"/usr/sbin/ip route add {description}")
-            if retcode != 0:
-                LOG.error(f"Failed adding route {description}:{format_stdout(stdout)}")
-
-
-def acquire_sysinv_agent_lock():
-    LOG.info("Acquiring lock to synchronize with sysinv-agent audit")
-    lock_file_fd = os.open(SYSINV_LOCK_FILE, os.O_CREAT | os.O_RDONLY)
-    return acquire_file_lock(lock_file_fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 5, 5)
-
-
-def release_sysinv_agent_lock(lockfd):
-    if lockfd:
-        LOG.info("Releasing lock")
-        release_file_lock(lockfd)
-        os.close(lockfd)
-
-
-def acquire_file_lock(lockfd, operation, max_retry, wait_interval):
-    count = 1
-    while count <= max_retry:
-        try:
-            fcntl.flock(lockfd, operation)
-            LOG.info("Successfully acquired lock (fd={})".format(lockfd))
-            return lockfd
-        except IOError as e:
-            # raise on unrelated IOErrors
-            if e.errno != errno.EAGAIN:
-                raise
-            LOG.info("Could not acquire lock({}): {} ({}/{}), will retry".format(
-                lockfd, str(e), count, max_retry))
-            time.sleep(wait_interval)
-            count += 1
-    LOG.error("Failed to acquire lock (fd={}). Stopped trying.".format(lockfd))
-    sys.exit(1)
-
-
-def release_file_lock(lockfd):
-    if lockfd:
-        fcntl.flock(lockfd, fcntl.LOCK_UN)
-
-
-def is_upgrade():
-    return os.path.isfile(UPGRADE_FILE)
-
-
-def update_interfaces():
-    new_config = get_new_config()
-
-    auto = new_config["auto"]
-    if len(auto) == 0 or (len(auto) == 1 and next(iter(auto)) == "lo"):
-        LOG.info(f"Generated {PUPPET_FILE} with empty configuration: '{' '.join(auto)}', exiting")
-        return None
-
-    disable_pxeboot_interface()
-
-    if is_upgrade():
-        LOG.info("Upgrade bootstrap is in execution")
-        return update_ifaces_online(new_config)
-
-    return update_ifaces_ifupdown(new_config)
-
-
-def disable_pxeboot_interface():
-    path = get_ifcfg_path("pxeboot")
-    if not os.path.isfile(path):
-        return
-
-    lines = read_file_lines(path)
-    _, ifaces = StanzaParser.ParseLines(lines)
-    if len(ifaces) == 0:
-        LOG.info(f"Pxeboot install config file '{path}' has no valid interface config, skipping")
-        return
-
-    for iface in ifaces.keys():
-        LOG.info(f"Turn off pxeboot install config for {iface}, will be turned on later")
-        set_iface_down(iface)
-
-    LOG.info("Remove ifcfg-pxeboot, left from kickstart install phase")
-    remove_iface_config_file("pxeboot")
-
-
-def update_ifaces_ifupdown(new_config):
-    current_config = get_current_config()
-    comparison = compare_configs(new_config, current_config)
-    down_list = get_down_list(current_config, comparison)
-    up_list = get_up_list(new_config, comparison)
-
-    lock = acquire_sysinv_agent_lock() if down_list or up_list else None
-    try:
-        set_ifaces_down(current_config, down_list)
-        remove_iface_config_files(comparison)
-        update_files(new_config)
-        set_ifaces_up(new_config, up_list)
-    finally:
-        release_sysinv_agent_lock(lock)
-
-    return get_updated_ifaces(new_config, up_list)
-
-
-def update_ifaces_online(config):
-    sorted_ifaces = sort_ifaces_by_type(config, config["auto"], ONLINE_ORDER)
-    if not sorted_ifaces:
-        return set()
-    update_files(config)
-    for iface in sorted_ifaces:
-        LOG.info(f"Configuring interface {iface}")
-        ensure_iface_configured(iface, config["ifaces"][iface])
-    return get_updated_ifaces(config, sorted_ifaces)
-
-
-def is_iface_missing_or_down(iface):
-    path = f"{DEVLINK_BASE_PATH}{iface}/operstate"
-    if os.path.isfile(path):
-        state = read_file_text(path)
-        if state != "down":
-            return False
-    return True
-
-
-def get_iface_address(iface, cfg):
-    if address := cfg.get("address", None):
-        if "/" not in address:
-            if netmask := cfg.get("netmask", None):
-                if ":" in address:
-                    try:
-                        prefixlen = int(netmask)
-                    except ValueError:
-                        LOG.error(f"Failed to get {iface} interface prefixlen, "
-                                  f"invalid value: '{netmask}'")
-                        return None
-                else:
-                    try:
-                        prefixlen = get_prefix_length(netmask)
-                    except InvalidNetmaskError as e:
-                        LOG.error(f"Failed to get {iface} interface netmask: {e}")
-                        return None
-                return f"{address}/{prefixlen}"
-            LOG.error(f"Interface {iface} has address but no netmask")
-            return None
-    return address
-
-
-def ensure_iface_configured_label(iface, cfg):
-    address = get_iface_address(iface, cfg)
-    if not address:
-        return
-    base_iface = get_base_iface(iface)
-    existing = get_link_addresses(base_iface)
-    if address in existing:
-        LOG.info(f"Link already has address '{address}', no need to set label up")
-    else:
-        if set_iface_up(iface) == 0:
-            return
-        add_ip_to_iface(base_iface, address)
-    if gateway := cfg.get("gateway", None):
-        add_default_route(base_iface, gateway)
-
-
-def ensure_iface_configured_non_label(iface, cfg):
-    if is_iface_missing_or_down(iface):
-        LOG.info(f"Interface '{iface}' is missing or down, flushing IPs and bringing up")
-        flush_ips(iface)
-        if set_iface_up(iface) == 0:
-            return
-    address = get_iface_address(iface, cfg)
-    if not address:
-        return
-    existing = get_link_addresses(iface)
-    if address not in existing:
-        add_ip_to_iface(iface, address)
-    if gateway := cfg.get("gateway", None):
-        add_default_route(iface, gateway)
-
-
-def ensure_iface_configured(iface, cfg):
-    if is_label(iface):
-        ensure_iface_configured_label(iface, cfg)
-    else:
-        ensure_iface_configured_non_label(iface, cfg)
-
-
-def get_link_addresses(name):
-    retcode, stdout = execute_system_cmd(f"/usr/sbin/ip -br addr show dev {name}")
-    if retcode == 0:
-        verbs = stdout.split()
-        return verbs[2:]
-    LOG.error(f"Failed to get IP address list from {name}:{format_stdout(stdout)}")
-    return None
-
-
-def add_ip_to_iface(iface, ip):
-    LOG.info(f"Adding IP {ip} to interface {iface}")
-    existing = get_link_addresses(iface)
-    if existing is None:
-        return
-    if ip in existing:
-        LOG.info(f"Interface {iface} already has address {ip}, skipping")
-        return
-    retcode, stdout = execute_system_cmd(f"/usr/sbin/ip addr add {ip} dev {iface}")
-    if retcode != 0:
-        LOG.error(f"Failed to add IP address to interface {iface}:{format_stdout(stdout)}")
-
-
-def add_default_route(iface, gateway):
-    route = {"network": "default",
-             "nexthop": gateway,
-             "ifname": iface}
-    add_route_to_kernel(route)
-
-
-def flush_ips(iface):
-    path = DEVLINK_BASE_PATH + iface
-    if os.path.islink(path):
-        retcode, stdout = execute_system_cmd(f"/usr/sbin/ip addr flush dev {iface}")
-        if retcode != 0:
-            LOG.error(f"Command 'ip addr flush' failed for interface {iface}:"
-                      f"{format_stdout(stdout)}")
-
-
-def write_routes_file(route_entries):
-    lines = [get_header()] + route_entries
-    with open(ETC_ROUTES_FILE, "w") as f:
-        f.write("\n".join(lines) + "\n")
-
-
-def update_routes(updated_ifaces=None):
-    if updated_ifaces is None:
-        updated_ifaces = set()
-
-    new_routes = get_route_entries([PUPPET_ROUTES_FILE, PUPPET_ROUTES6_FILE])
-    new_routes_set = set(new_routes)
-
-    current_routes = get_route_entries([ETC_ROUTES_FILE])
-    current_routes_set = set(current_routes)
-
-    write_routes_file(new_routes)
-
-    if new_routes_set != current_routes_set:
-        LOG.info(f"Differences found between {PUPPET_ROUTES_FILE} and {ETC_ROUTES_FILE}")
-        # Remove routes that are currently present and no longer needed, following the order in
-        # which they appear in the file
-        for route_entry in current_routes:
-            if route_entry not in new_routes_set:
-                remove_route_entry_from_kernel(route_entry)
-    else:
-        LOG.info(f"No differences found between {PUPPET_ROUTES_FILE} and {ETC_ROUTES_FILE}")
-        if not updated_ifaces:
-            return
-
-    for route_entry in new_routes:
-        if route_entry not in current_routes_set:
-            LOG.info(f"Route not previously present in {ETC_ROUTES_FILE}, adding")
-        elif get_route_iface(route_entry) in updated_ifaces:
-            LOG.info("Route is associated with and updated interface, adding")
-        else:
-            continue
-        add_route_entry_to_kernel(route_entry)
-
-
-def check_enrollment_config():
-    if not os.path.isfile(SUBCLOUD_ENROLLMENT_FILE) or not os.path.isfile(CLOUD_INIT_FILE):
-        return
-    LOG.info(f"Enrollment: Parsing file '{CLOUD_INIT_FILE}'")
-    lines = read_file_lines(CLOUD_INIT_FILE)
-    _, ifaces = StanzaParser.ParseLines(lines)
-    ifaces.pop("lo", None)
-    if len(ifaces) == 0:
-        LOG.warning(f"Enrollment: Could not find any valid interface config in '{CLOUD_INIT_FILE}'")
-        return
-    ifaces_with_gateway = dict()
-    for iface, cfg in ifaces.items():
-        if gateway := cfg.get("gateway", None):
-            try:
-                gateway_ip = IPAddress(gateway)
-            except AddrFormatError:
-                LOG.warning(f"Enrollment: Invalid gateway address '{gateway}' "
-                            f"for interface '{iface}'")
-                continue
-            ifaces_with_gateway.setdefault(gateway_ip.version, dict())[iface] = cfg
-    if len(ifaces_with_gateway) == 0:
-        LOG.warning("Enrollment: No interface with gateway address found, skipping")
-        return
-    for version, iface_cfgs in ifaces_with_gateway.items():
-        if len(iface_cfgs) > 1:
-            LOG.warning(f"Enrollment: Multiple interfaces with gateway for ipv{version} found: "
-                        f"{', '.join(iface_cfgs.keys())}")
-        for iface, cfg in iface_cfgs.items():
-            LOG.info(f"Enrollment: Configuring interface {iface} with gateway {cfg['gateway']}")
-            ensure_iface_configured(iface, cfg)
-
-
-def main():
-    log_format = ('%(asctime)s: [%(process)s]: %(filename)s(%(lineno)s): '
-                  '%(levelname)s: %(message)s')
-    LOG.basicConfig(filename=LOG_FILE, format=log_format, level=LOG.INFO, datefmt="%FT%T")
-
-    parser = argparse.ArgumentParser(
-        prog='Network Configuration Applier',
-        description='Applies the network configuration generated by Puppet to the linux kernel'
-    )
-    parser.add_argument("--routes", action='store_true')
-    args = parser.parse_args()
-
-    apply_config(args.routes)
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main())
diff --git a/puppet-manifests/src/bin/apply_network_config.sh b/puppet-manifests/src/bin/apply_network_config.sh
index e478833d3..0feedf633 100755
--- a/puppet-manifests/src/bin/apply_network_config.sh
+++ b/puppet-manifests/src/bin/apply_network_config.sh
@@ -7,8 +7,6 @@
 #
 ################################################################################
 
-# WARNING: This file is OBSOLETE, use apply_network_config.py instead
-
 #
 #  Purpose of this script is to copy the puppet-built ifcfg-* network config
 #  files from the PUPPET_DIR to the ETC_DIR. Only files that are detected as
diff --git a/puppet-manifests/src/bin/k8s_wait_for_endpoints_health.py b/puppet-manifests/src/bin/k8s_wait_for_endpoints_health.py
index 0e235855f..fa391d321 100644
--- a/puppet-manifests/src/bin/k8s_wait_for_endpoints_health.py
+++ b/puppet-manifests/src/bin/k8s_wait_for_endpoints_health.py
@@ -62,10 +62,10 @@ def k8s_wait_for_endpoints_health(tries=TRIES, try_sleep=TRY_SLEEP, timeout=TIME
     healthz_endpoints = [APISERVER_READYZ_ENDPOINT, CONTROLLER_MANAGER_HEALTHZ_ENDPOINT,
                          SCHEDULER_HEALTHZ_ENDPOINT, KUBELET_HEALTHZ_ENDPOINT]
     for endpoint in healthz_endpoints:
-        is_k8s_endpoint_healthy = kubernetes.k8s_health_check(tries=tries,
-                                                              try_sleep=try_sleep,
-                                                              timeout=timeout,
-                                                              healthz_endpoint=endpoint)
+        is_k8s_endpoint_healthy = kubernetes.k8s_health_check(tries = tries,
+                                                              try_sleep = try_sleep,
+                                                              timeout = timeout,
+                                                              healthz_endpoint = endpoint)
         if not is_k8s_endpoint_healthy:
             LOG.error("Timeout: Kubernetes control-plane endpoints not healthy")
             return 1
@@ -93,3 +93,4 @@ def main():
 
 if __name__ == "__main__":
     sys.exit(main())
+
diff --git a/puppet-manifests/src/bin/network_ifupdown.sh b/puppet-manifests/src/bin/network_ifupdown.sh
index 1a0412e58..88b86f409 100644
--- a/puppet-manifests/src/bin/network_ifupdown.sh
+++ b/puppet-manifests/src/bin/network_ifupdown.sh
@@ -5,8 +5,6 @@
 #
 ################################################################################
 
-# WARNING: This file is OBSOLETE
-
 #
 # This file purpose is to provide helper functions if the system is Debian based
 # for the apply_network_config.sh script
diff --git a/puppet-manifests/src/bin/network_sysconfig.sh b/puppet-manifests/src/bin/network_sysconfig.sh
index 003ee70f8..bf6cd890e 100644
--- a/puppet-manifests/src/bin/network_sysconfig.sh
+++ b/puppet-manifests/src/bin/network_sysconfig.sh
@@ -5,8 +5,6 @@
 #
 ################################################################################
 
-# WARNING: This file is OBSOLETE
-
 #
 # This file purpose is to provide helper functions if the system is CentOS based
 # for the apply_network_config.sh script
diff --git a/puppet-manifests/src/bin/puppet-update-grub-env.py b/puppet-manifests/src/bin/puppet-update-grub-env.py
index 686859822..18c5d8352 100755
--- a/puppet-manifests/src/bin/puppet-update-grub-env.py
+++ b/puppet-manifests/src/bin/puppet-update-grub-env.py
@@ -28,7 +28,6 @@ import sys
 BOOT_ENV = "/boot/efi/EFI/BOOT/boot.env"
 KERNEL_PARAMS_STRING = "kernel_params"
 
-
 # Get value of kernel_params from conf
 def read_kernel_params(conf):
     """Get value of kernel_params from conf"""
@@ -47,7 +46,6 @@ def read_kernel_params(conf):
 
     return res
 
-
 # Write key=value string to conf
 def write_conf(conf, string):
     """Write key=value string to conf"""
@@ -61,7 +59,6 @@ def write_conf(conf, string):
         print(err)
         raise
 
-
 def set_parser():
     """Set command parser"""
 
@@ -113,7 +110,6 @@ def set_parser():
 
     return parser
 
-
 def convert_dict_to_value(kernel_params_dict):
     """Dictionary to value"""
 
@@ -132,7 +128,6 @@ def convert_dict_to_value(kernel_params_dict):
 
     return f"kernel_params={kernel_params}"
 
-
 def convert_value_to_dict(value):
     """Value to dictionary"""
 
@@ -161,6 +156,7 @@ def convert_value_to_dict(value):
         else:
             key, val = param, ''
 
+
         kernel_params_dict[key] = val
 
     if hugepage_cache:
@@ -181,7 +177,6 @@ def convert_value_to_dict(value):
 
     return kernel_params_dict
 
-
 def edit_boot_env(args):
     """Edit boot environment"""
 
@@ -217,7 +212,6 @@ def edit_boot_env(args):
     kernel_params = convert_dict_to_value(kernel_params_dict)
     write_conf(BOOT_ENV, kernel_params)
 
-
 def get_kernel_dir():
     """Get kernel directory"""
 
@@ -229,12 +223,11 @@ def get_kernel_dir():
 
     return "/boot/1"
 
-
 def edit_kernel_env(args):
     """Edit kernel environment"""
 
     kernel_dir = get_kernel_dir()
-    path_all = os.path.join(kernel_dir, "vmlinuz*-amd64")
+    path_all = os.path.join(kernel_dir,"vmlinuz*-amd64")
     path_rt = os.path.join(kernel_dir, "vmlinuz*rt*-amd64")
 
     glob_all_kernels = [os.path.basename(f) for f in glob.glob(path_all)]
@@ -261,7 +254,6 @@ def edit_kernel_env(args):
     kernel_rollback_env = f"kernel_rollback={kernel}"
     write_conf(kernel_env, kernel_rollback_env)
 
-
 def list_kernels():
     """List kernels"""
 
@@ -280,7 +272,6 @@ def list_kernels():
 
     print(output)
 
-
 def list_kernel_params():
     """List kernel params"""
 
@@ -297,7 +288,6 @@ def list_kernel_params():
             print(line)
             break
 
-
 def main():
     """Main"""
     parser = set_parser()
@@ -315,6 +305,5 @@ def main():
     if args.list_kernel_params:
         list_kernel_params()
 
-
 if __name__ == "__main__":
     main()
diff --git a/puppet-manifests/src/modules/platform/files/change_k8s_control_plane_params.py b/puppet-manifests/src/modules/platform/files/change_k8s_control_plane_params.py
index 66b595fac..f531088da 100644
--- a/puppet-manifests/src/modules/platform/files/change_k8s_control_plane_params.py
+++ b/puppet-manifests/src/modules/platform/files/change_k8s_control_plane_params.py
@@ -729,15 +729,14 @@ def get_kubelet_cfg_from_service_parameters(service_params):
         # map[string]string & []string
         if value.startswith(('[', '{')) and value.endswith((']', '}')):
             try:
-                value = json.loads(
-                    value.replace('True', 'true').replace('False', 'false').replace("'", '"'))
+                value = json.loads(value.replace('True', 'true').replace('False', 'false').replace("'", '"'))
             except Exception as e:
                 msg = 'Parsing param: %s / value: %s. [Error: %s]' % (param, value, e)
                 LOG.error(msg)
                 return 3
         # bool
         elif value in ['False', 'false'] or value in ['True', 'true']:
-            value = True if value in ['True', 'true'] else False  # pylint: disable-msg=simplifiable-if-expression # noqa: E501
+            value = True if value in ['True', 'true'] else False  # pylint: disable-msg=simplifiable-if-expression
         # float
         elif '.' in value:
             try:
@@ -1158,8 +1157,7 @@ def main():
     parser.add_argument("--kubelet_latest_config_file", default="/var/lib/kubelet/config.yaml")
     parser.add_argument("--kubelet_bak_config_file", default="/var/lib/kubelet/config.yaml.bak")
     parser.add_argument("--kubelet_error_log", default="/tmp/kubelet_errors.log")
-    parser.add_argument("--k8s_configmaps_init_flag",
-                        default="/tmp/.sysinv_k8s_configmaps_initialized")
+    parser.add_argument("--k8s_configmaps_init_flag", default="/tmp/.sysinv_k8s_configmaps_initialized")
 
     parser.add_argument("--automatic_recovery", default=True)
     parser.add_argument("--timeout", default=RECOVERY_TIMEOUT)
diff --git a/puppet-manifests/src/modules/platform/manifests/network.pp b/puppet-manifests/src/modules/platform/manifests/network.pp
index c60c32864..57779d85b 100644
--- a/puppet-manifests/src/modules/platform/manifests/network.pp
+++ b/puppet-manifests/src/modules/platform/manifests/network.pp
@@ -800,7 +800,7 @@ class platform::network::apply {
   -> Exec['apply-network-config']
 
   exec {'apply-network-config':
-    command => 'apply_network_config.py',
+    command => 'apply_network_config.sh',
   }
 
   # Wait for network interface to leave tentative state during ipv6 DAD, if interface is UP
@@ -890,7 +890,7 @@ class platform::network::routes::runtime {
   }
 
   exec {'apply-network-config route setup':
-    command => 'apply_network_config.py --routes',
+    command => 'apply_network_config.sh --routes',
   }
 }
 
diff --git a/puppet-manifests/test-requirements.txt b/puppet-manifests/test-requirements.txt
deleted file mode 100644
index 31b716025..000000000
--- a/puppet-manifests/test-requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-mock>=2.0.0
-stestr>=1.0.0
-netaddr
diff --git a/puppet-manifests/tests/__init__.py b/puppet-manifests/tests/__init__.py
deleted file mode 100644
index e4937264c..000000000
--- a/puppet-manifests/tests/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Copyright (c) 2025 Wind River Systems, Inc.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
diff --git a/puppet-manifests/tests/filesystem_mock.py b/puppet-manifests/tests/filesystem_mock.py
deleted file mode 100644
index 109c4b077..000000000
--- a/puppet-manifests/tests/filesystem_mock.py
+++ /dev/null
@@ -1,318 +0,0 @@
-#
-# Copyright (c) 2025 Wind River Systems, Inc.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-import io
-
-# Keys for filesystem node properties
-PARENT = "parent"
-TYPE = "type"
-FILE = "file"
-DIR = "dir"
-LINK = "link"
-CONTENTS = "contents"
-TARGET = "target"
-REF = "ref"
-LISTENERS = "listeners"
-
-
-class FilesystemMockError(BaseException):
-    pass
-
-
-class FileMock():
-    def __init__(self, fs, entry):
-        self.fs = fs
-        self.entry = entry
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        pass
-
-    def readlines(self):
-        lines = self.entry[CONTENTS].split("\n")
-        out_lines = [line + "\n" for line in lines[:-1]]
-        if len(lines[-1]) > 0:
-            out_lines.append(lines[-1])
-        return out_lines
-
-    def read(self):
-        return self.entry[CONTENTS]
-
-    def write(self, contents):
-        if REF not in self.entry:
-            raise io.UnsupportedOperation("not writable")
-        self.entry[CONTENTS] += contents
-
-
-class ReadOnlyFileContainer():
-    def __init__(self, contents=None):
-        self.next_id = 0
-        self.root = self._get_new_dir(None)
-        if contents:
-            self.batch_add(contents)
-
-    def batch_add(self, contents):
-        for path, data in contents.items():
-            if data is None:
-                self._add_dir(path)
-            elif type(data) == str:
-                self._add_file(path, data)
-            elif type(data) == tuple and len(data) == 1 and type(data[0]) == str:
-                self._add_link(path, data[0])
-            else:
-                raise FilesystemMockError("Invalid entry, must be None for directory, "
-                                          "str for file or tuple with 1 str element for link")
-
-    def get_root_node(self):
-        return self.root
-
-    @staticmethod
-    def _get_new_dir(parent):
-        return {PARENT: parent, TYPE: DIR, CONTENTS: dict()}
-
-    @staticmethod
-    def _get_new_file(parent, contents):
-        return {PARENT: parent, TYPE: FILE, CONTENTS: contents}
-
-    @staticmethod
-    def _get_new_link(parent, entry, target_path):
-        return {PARENT: parent, TYPE: LINK, CONTENTS: entry, TARGET: target_path}
-
-    def _do_add_dir(self, path_pieces):
-        def add_dir_rec(parent, pieces):
-            if len(pieces) == 0:
-                return parent
-            current = parent[CONTENTS].get(pieces[0], None)
-            if not current:
-                current = self._get_new_dir(parent)
-                parent[CONTENTS][pieces[0]] = current
-            return add_dir_rec(current, pieces[1:])
-        return add_dir_rec(self.root, path_pieces)
-
-    def _get_entry(self, path):
-        pieces = path.split("/")[1:]
-
-        def get_entry_rec(parent, pieces):
-            if len(pieces) == 0:
-                return parent
-            current = parent[CONTENTS].get(pieces[0], None)
-            if not current:
-                raise FilesystemMockError(f"Path not found: '{path}'")
-            return get_entry_rec(current, pieces[1:])
-
-        return get_entry_rec(self.root, pieces)
-
-    def _add_dir(self, path):
-        pieces = path.split("/")[1:]
-        self._do_add_dir(pieces)
-
-    def _add_file(self, path, contents):
-        pieces = path.split("/")[1:]
-        new_dir = self._do_add_dir(pieces[:-1])
-        file_entry = self._get_new_file(new_dir, contents)
-        new_dir[CONTENTS][pieces[-1]] = file_entry
-
-    def _add_link(self, path, ref_path):
-        pieces = path.split("/")[1:]
-        new_dir = self._do_add_dir(pieces[:-1])
-        ref_entry = self._get_entry(ref_path)
-        link_entry = self._get_new_link(new_dir, ref_entry, ref_path)
-        new_dir[CONTENTS][pieces[-1]] = link_entry
-
-
-class FilesystemMock():
-    def __init__(self, contents: dict = None, fs: ReadOnlyFileContainer = None):
-        if fs is not None:
-            self.fs = fs
-            add_contents = True
-        else:
-            self.fs = ReadOnlyFileContainer(contents)
-            add_contents = False
-
-        self.root = self._get_new_entry(self.fs.get_root_node(), None)
-        if add_contents and contents:
-            self.batch_add(contents)
-
-    def batch_add(self, contents):
-        for path, data in contents.items():
-            if data is None:
-                self.create_directory(path)
-            elif type(data) == str:
-                self.set_file_contents(path, data)
-            elif type(data) == tuple and len(data) == 1 and type(data[0]) == str:
-                self.set_link_contents(path, data[0])
-            else:
-                raise FilesystemMockError("Invalid entry, must be None for directory, "
-                                          "str for file or tuple with 1 str element for link")
-
-    @staticmethod
-    def _get_new_entry(ref, parent, node_type=None):
-        if not node_type:
-            node_type = ref[TYPE]
-        entry = {REF: ref, PARENT: parent, TYPE: node_type}
-        if node_type == DIR:
-            entry[CONTENTS] = ref[CONTENTS].copy() if ref else dict()
-        elif node_type == LINK:
-            entry[CONTENTS] = ref[CONTENTS] if ref else None
-            entry[TARGET] = ref[TARGET] if ref else None
-        else:
-            entry[CONTENTS] = ''
-        return entry
-
-    def _get_entry(self, path, translate_link=False):
-        pieces = path.split("/")[1:]
-
-        def get_entry_rec(contents, pieces):
-            if len(pieces) == 0:
-                if translate_link and contents[TYPE] == LINK:
-                    return contents[CONTENTS]
-                return contents
-            if contents[TYPE] == LINK:
-                contents = contents[CONTENTS]
-            if REF in contents and contents[CONTENTS] is None:
-                child = contents[REF][CONTENTS].get(pieces[0], None)
-            else:
-                child = contents[CONTENTS].get(pieces[0], None)
-            if child is None:
-                return None
-            return get_entry_rec(child, pieces[1:])
-
-        return get_entry_rec(self.root, pieces)
-
-    def _patch_entry(self, path, node_type):
-        pieces = path.split("/")[1:]
-
-        def translate_link(entry):
-            target = entry[CONTENTS]
-            if REF not in target:
-                target = self._patch_entry(entry[TARGET], target[TYPE])
-                entry[CONTENTS] = target
-            return target
-
-        def patch_entry_rec(level, entry, pieces):
-            if len(pieces) == 0:
-                if entry[TYPE] == LINK and node_type != LINK:
-                    entry = translate_link(entry)
-                if entry[TYPE] != node_type:
-                    if node_type == FILE:
-                        raise IsADirectoryError(f"[Errno 21] Is a directory: '{path}'")
-                    raise NotADirectoryError(f"[Errno 20] Not a directory: '{path}'")
-                return entry
-            if entry[TYPE] == LINK:
-                entry = translate_link(entry)
-            if entry[TYPE] != DIR:
-                raise NotADirectoryError(f"[Errno 20] Not a directory: '{path}'")
-            if entry[CONTENTS] is None:
-                entry[CONTENTS] = entry[REF][CONTENTS].copy()
-            child = entry[CONTENTS].get(pieces[0], None)
-            if child is None or REF not in child:
-                if child is None:
-                    new_type = node_type if len(pieces) == 1 else DIR
-                    child = self._get_new_entry(None, entry, new_type)
-                else:
-                    child = self._get_new_entry(child, entry)
-                entry[CONTENTS][pieces[0]] = child
-            return patch_entry_rec(level + 1, child, pieces[1:])
-
-        return patch_entry_rec(0, self.root, pieces)
-
-    def exists(self, path):
-        entry = self._get_entry(path)
-        return entry is not None
-
-    def isfile(self, path):
-        entry = self._get_entry(path)
-        return entry and entry[TYPE] == FILE
-
-    def isdir(self, path):
-        entry = self._get_entry(path)
-        return entry and entry[TYPE] == DIR
-
-    def islink(self, path):
-        entry = self._get_entry(path)
-        return entry and entry[TYPE] == LINK
-
-    def open(self, path, mode="r"):
-        if "w" in mode:
-            entry = self._patch_entry(path, FILE)
-        else:
-            entry = self._get_entry(path, translate_link=True)
-            if not entry:
-                raise FileNotFoundError(f"[Errno 2] No such file or directory: '{path}'")
-        if entry[TYPE] == DIR:
-            raise IsADirectoryError(f"[Errno 21] Is a directory: '{path}'")
-        if "w" in mode:
-            self._call_listeners(entry)
-        return FileMock(self, entry)
-
-    def _call_listeners(self, entry):
-        if parent := entry[PARENT]:
-            self._call_listeners(parent)
-        if listeners := entry.get(LISTENERS, None):
-            for listener in listeners:
-                listener()
-
-    def create_directory(self, path):
-        entry = self._patch_entry(path, DIR)
-        self._call_listeners(entry)
-
-    def set_file_contents(self, path, contents):
-        entry = self._patch_entry(path, FILE)
-        entry[CONTENTS] = contents
-        self._call_listeners(entry)
-
-    def get_file_contents(self, path):
-        entry = self._get_entry(path, translate_link=True)
-        if entry is None:
-            raise FilesystemMockError("Path does not exist")
-        if entry[TYPE] != FILE:
-            raise FilesystemMockError("Path is not a file")
-        return entry[CONTENTS]
-
-    def set_link_contents(self, link_path, target_path):
-        target = self._get_entry(target_path)
-        if target is None:
-            raise FilesystemMockError("Target path does not exist")
-        entry = self._patch_entry(link_path, LINK)
-        entry[CONTENTS] = target
-        entry[TARGET] = target_path
-        self._call_listeners(entry)
-
-    def get_file_list(self, path):
-        entry = self._get_entry(path, translate_link=True)
-        if entry is None:
-            raise FilesystemMockError("Path does not exist")
-        if entry[TYPE] != DIR:
-            raise FilesystemMockError("Path is not a directory")
-        files = []
-        for name, child in entry[CONTENTS].items():
-            if child[TYPE] == FILE:
-                files.append(name)
-        files.sort()
-        return files
-
-    def add_listener(self, path, callback):
-        entry = self._get_entry(path, translate_link=True)
-        if entry is None:
-            raise FilesystemMockError("Path does not exist")
-        if REF not in entry:
-            entry = self._patch_entry(path, entry[TYPE])
-        listeners = entry.setdefault(LISTENERS, list())
-        listeners.append(callback)
-
-    def delete(self, path):
-        pieces = path.split("/")[1:]
-
-        entry = self._get_entry(path)
-        if entry is None:
-            raise FileNotFoundError(f"[Errno 2] No such file or directory: '{path}'")
-
-        pieces = path.split("/")
-        patched_entry = self._patch_entry("/".join(pieces[:-1]), DIR)
-        patched_entry[CONTENTS].pop(pieces[-1])
-        self._call_listeners(patched_entry)
diff --git a/puppet-manifests/tests/system_cmd_test_script.sh b/puppet-manifests/tests/system_cmd_test_script.sh
deleted file mode 100755
index 2932829af..000000000
--- a/puppet-manifests/tests/system_cmd_test_script.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-
-################################################################################
-# Copyright (c) 2025 Wind River Systems, Inc.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-################################################################################
-
-#
-# This script is used by the automated tests
-# tests.test_apply_network_config.GeneralTests.test_execute_system_cmd_timeout_*, it simulates a
-# command that takes too long to terminate and triggers a timeout. In certain situations, the ifup
-# command can exhibit this behavior.
-#
-
-return_code=$1
-extra_sleep=$2
-
-terminate()
-{
-    echo "< SIGTERM RECEIVED >"
-
-    if [[ "$extra_sleep" == "-e" ]]; then
-        sleep 10
-        echo "< AFTER EXTRA SLEEP >"
-    fi
-
-    exit $return_code
-}
-
-trap terminate 15
-
-echo "< BEFORE SLEEP >"
-sleep 10
-echo "< AFTER SLEEP >"
diff --git a/puppet-manifests/tests/test_apply_network_config.py b/puppet-manifests/tests/test_apply_network_config.py
deleted file mode 100644
index 069992545..000000000
--- a/puppet-manifests/tests/test_apply_network_config.py
+++ /dev/null
@@ -1,3062 +0,0 @@
-#
-# Copyright (c) 2025 Wind River Systems, Inc.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-import mock
-import os
-import re
-import testtools
-from netaddr import IPAddress
-from netaddr import IPNetwork
-from netaddr import AddrFormatError
-
-from tests.filesystem_mock import FilesystemMock
-from tests.filesystem_mock import ReadOnlyFileContainer
-import src.bin.apply_network_config as anc
-
-
-class NetworkingMockError(BaseException):
-    pass
-
-
-class NetworkingMock():  # pylint: disable=too-many-instance-attributes
-    def __init__(self, fs: FilesystemMock, ifaces: list):
-        self._stdout = ''
-        self._history = []
-        self._etc_changed = True
-        self._fs = fs
-        self._current_config = None
-        self._links = dict()
-        self._routes = dict()
-        self._next_route_id = 0
-        self._allow_multiple_default_gateways = False
-        self._add_eth_ifaces(ifaces)
-        self._fs.add_listener(anc.ETC_DIR, self._etc_dir_changed)
-
-    def _etc_dir_changed(self):
-        self._etc_changed = True
-
-    def _add_eth_ifaces(self, ifaces):
-        for iface in ifaces:
-            self._add_eth_iface(iface)
-
-    @staticmethod
-    def _get_device_path(iface, is_virtual=False):
-        if is_virtual:
-            return f"/sys/devices/virtual/net/{iface}"
-        return f"/sys/devices/pci0000:00/net/{iface}"
-
-    def _add_eth_iface(self, iface):
-        phys_path = self._get_device_path(iface)
-        self._fs.set_file_contents(phys_path + "/operstate", "down")
-        self._fs.set_link_contents(anc.DEVLINK_BASE_PATH + iface, phys_path)
-        self._links[iface] = {"adm_state": False, "virtual": False,
-                              "addresses": set(), "routes": set()}
-
-    def _parse_etc_interfaces(self):
-        file_list = self._fs.get_file_list(anc.ETC_DIR)
-        parser = anc.StanzaParser()
-        for file in file_list:
-            file_contents = self._fs.get_file_contents(anc.ETC_DIR + "/" + file)
-            parser.parse_lines(file_contents.split("\n"))
-        return parser.get_auto_and_ifaces()
-
-    @staticmethod
-    def _decode_iface_config(name, config):
-        if anc.is_label(name):
-            parent = name.split(":")[0]
-            props = {"type": anc.LABEL, "parent": parent}
-        elif name == "lo":
-            props = {"type": anc.LO}
-        elif vlan_attribs := anc.get_vlan_attributes(name, config):
-            preup = config.get("pre-up", None)
-            add_link_cmd = preup and "ip link add" in preup
-            props = {"type": anc.VLAN, "raw_dev": vlan_attribs[0], "vlan_id": vlan_attribs[1],
-                     "add_link_cmd": add_link_cmd}
-        elif slaves := config.get("bond-slaves", None):
-            props = {"type": anc.BONDING, "slaves": slaves.split()}
-        elif master := config.get("bond-master", None):
-            props = {"type": anc.SLAVE, "master": master}
-        else:
-            props = {"type": anc.ETH}
-        mode = config["iface"].split()[2]
-        props["mode"] = mode
-        if mode == "static":
-            if not (address := config.get("address", None)):
-                raise NetworkingMockError(
-                    f"Interface '{name}' is set to STATIC but has no address specified")
-            if "/" in address:
-                props["address"] = IPNetwork(address)
-            else:
-                if not (netmask := config.get("netmask", None)):
-                    raise NetworkingMockError(
-                        f"Interface '{name}' is set to STATIC but has no netmask specified")
-                props["address"] = IPNetwork(f"{address}/{netmask}")
-            if gateway := config.get("gateway", None):
-                props["gateway"] = IPAddress(gateway)
-        return props
-
-    def _decode_config(self):
-        auto, etc_ifaces = self._parse_etc_interfaces()
-        decoded_ifaces = dict()
-        for iface, config in etc_ifaces.items():
-            decoded_ifaces[iface] = self._decode_iface_config(iface, config)
-        return auto, decoded_ifaces
-
-    @staticmethod
-    def _get_ifaces_by_type(ifaces):
-        type_ifaces = dict()
-        for iface, config in ifaces.items():
-            iflist = type_ifaces.setdefault(config["type"], list())
-            iflist.append(iface)
-        for iftype in type_ifaces.keys():
-            type_ifaces[iftype].sort()
-        return type_ifaces
-
-    def _update_config(self):
-        if not self._etc_changed:
-            return
-        self._etc_changed = False
-        auto, ifaces = self._decode_config()
-        by_type = self._get_ifaces_by_type(ifaces)
-        self._current_config = {"auto": auto, "ifaces": ifaces, "by_type": by_type}
-
-    @staticmethod
-    def _sort_auto_by_type(config):
-        auto = set(config["auto"])
-        by_type = config["by_type"]
-        sorted_ifaces = list()
-        for iftype in [anc.LO, anc.ETH, anc.BONDING, anc.VLAN, anc.LABEL]:
-            if ifaces := by_type.get(iftype, None):
-                for iface in ifaces:
-                    if iface in auto:
-                        sorted_ifaces.append(iface)
-        return sorted_ifaces
-
-    def _add_route_line(self, line):
-        pieces = line.split()
-        if len(pieces) < 4:
-            raise NetworkingMockError(f"Invalid route in '{anc.ETC_ROUTES_FILE}' file: '{line}'")
-        netmask_ip = IPAddress(pieces[1])
-        prefixlen = netmask_ip.netmask_bits()
-        network = f"{pieces[0]}/{prefixlen}"
-        metric = pieces[5] if len(pieces) > 4 else None
-        self._do_ip_route_add(network, pieces[2], pieces[3], metric)
-
-    def _apply_etc_routes(self):
-        if not self._fs.isfile(anc.ETC_ROUTES_FILE):
-            return
-        file_contents = self._fs.get_file_contents(anc.ETC_ROUTES_FILE)
-        lines = [line.strip() for line in file_contents.split("\n")]
-        for line in lines:
-            clean_line = line.strip()
-            if clean_line and not clean_line.startswith("#"):
-                self._add_route_line(line)
-
-    def apply_auto(self):
-        self._reset_stdout()
-        self._etc_changed = True
-        self._update_config()
-        sorted_auto = self._sort_auto_by_type(self._current_config)
-        for iface in sorted_auto:
-            self._do_ifup(iface)
-        self._apply_etc_routes()
-        return self._stdout
-
-    def reset_history(self):
-        self._history = []
-
-    def get_history(self):
-        return self._history
-
-    def _add_history(self, command, *args):
-        self._history.append((command, *args))
-
-    def _reset_stdout(self):
-        self._stdout = ''
-
-    def _print_stdout(self, msg):
-        self._stdout += msg + "\n"
-
-    def _is_up(self, iface):
-        state_file_path = anc.IFSTATE_BASE_PATH + iface
-        if self._fs.isfile(state_file_path):
-            data = self._fs.get_file_contents(state_file_path)
-            return data == iface
-        return False
-
-    def _set_link_state(self, iface, link, state):
-        if link["adm_state"] == state:
-            return
-        link["adm_state"] = state
-        operstate_path = self._get_device_path(iface, link["virtual"]) + "/operstate"
-        value = "up" if state else "down"
-        self._fs.set_file_contents(operstate_path, value)
-
-    def _create_virtual_link(self, name):
-        if link := self._links.get(name, None):
-            self._print_stdout("RTNETLINK answers: File exists")
-            return link, 1
-        phys_path = self._get_device_path(name, True)
-        self._fs.set_file_contents(phys_path + "/operstate", "down")
-        self._fs.set_link_contents(anc.DEVLINK_BASE_PATH + name, phys_path)
-        link = {"adm_state": False, "virtual": True, "addresses": set(), "routes": set()}
-        self._links[name] = link
-        return link, 0
-
-    def _remove_virtual_link(self, name):
-        link, retcode = self._get_link(name)
-        if retcode != 0:
-            return 1
-        for route_id in link["routes"]:
-            self._routes.pop(route_id)
-        if deps := link.get("deps", None):
-            for dep in deps:
-                self._remove_virtual_link(dep)
-        del self._links[name]
-        self._fs.delete(anc.DEVLINK_BASE_PATH + name)
-        self._fs.delete(self._get_device_path(name, True))
-        return 0
-
-    def _get_link(self, name):
-        link = self._links.get(name, None)
-        if link:
-            return link, 0
-        self._print_stdout(f'Cannot find device "{name}"')
-        return None, 1
-
-    def _get_link_for_ip_cmd(self, name):
-        link = self._links.get(name, None)
-        if link:
-            return link, 0
-        self._print_stdout(f'Device "{name}" does not exist.')
-        return None, 1
-
-    def _enslave_iface(self, iface, master, master_failed):
-        if master_failed or not (link := self._links.get(iface, None)):
-            self._print_stdout(f"Failed to enslave {iface} to {master}. "
-                               f"Is {master} ready and a bonding interface ?")
-            return None, 1
-        link["master"] = master
-        self._set_link_state(iface, link, True)
-        return link, 0
-
-    def _unenslave_iface(self, iface):
-        if not (link := self._links.get(iface, None)):
-            return 1
-        link.pop("master", None)
-        self._set_link_state(iface, link, False)
-        return 0
-
-    def _add_address(self, iface, config, link):
-        mode = config["mode"]
-        if mode == "static":
-            address = config["address"]
-            if address in link["addresses"]:
-                self._print_stdout(f"Error: ipv{address.version}: Address already assigned.")
-                return 1
-            link["addresses"].add(address)
-            if gateway := config.get("gateway", None):
-                self._add_default_gateway(iface, link, gateway)
-        return 0
-
-    def _remove_routes_associated_to_address(self, link, address):
-        to_remove = []
-        for route_id in link["routes"]:
-            route = self._routes[route_id]
-            if route["via"] in address:
-                to_remove.append(route_id)
-        for route_id in to_remove:
-            self._routes.pop(route_id)
-            link["routes"].remove(route_id)
-
-    def _remove_address(self, config, link):
-        mode = config["mode"]
-        if mode == "static":
-            address = config["address"]
-            if address not in link["addresses"]:
-                self._print_stdout(f"Error: ipv{address.version}: Address not found.")
-                return 1
-            link["addresses"].remove(address)
-            self._remove_routes_associated_to_address(link, address)
-        return 0
-
-    def _add_default_gateway(self, ifname, link, gateway):
-        net = '0.0.0.0/0' if gateway.version == 4 else '::0/0'
-        route_filter = self._get_route_filter(net, None, None, None)
-        existing = self._find_routes(route_filter, True)
-        if existing:
-            if not self._allow_multiple_default_gateways:
-                raise NetworkingMockError("Trying to create default route from ifup for "
-                                          f"interface '{ifname}', default route already exists")
-        route_obj = self._get_route_obj(net, gateway, ifname, None)
-        retcode = self._check_can_add_route(route_obj, link)
-        if retcode != 0:
-            return retcode
-        for route_id in existing.keys():
-            self._remove_route(route_id)
-        self._add_route(route_obj, link)
-        return 0
-
-    def _set_lo_up(self, iface, config):
-        return self._set_eth_up(iface, config)
-
-    def _set_lo_down(self, iface, config):
-        return self._set_eth_down(iface, config)
-
-    def _set_eth_up(self, iface, config):
-        link, retcode = self._get_link(iface)
-        if retcode != 0:
-            return 1
-        self._set_link_state(iface, link, True)
-        return self._add_address(iface, config, link)
-
-    def _set_eth_down(self, iface, config):
-        link, retcode = self._get_link(iface)
-        if retcode != 0:
-            return 0
-        self._set_link_state(iface, link, False)
-        self._remove_address(config, link)
-        return 0
-
-    def _set_slave_up(self, iface, config):  # pylint: disable=no-self-use,unused-argument
-        raise NetworkingMockError(
-            f"ifup is not supposed to be called for slave interfaces: {iface}")
-
-    def _set_slave_down(self, iface, config):  # pylint: disable=no-self-use,unused-argument
-        raise NetworkingMockError(
-            f"ifdown is not supposed to be called for slave interfaces: {iface}")
-
-    def _set_bonding_up(self, iface, config):
-        link, retcode = self._create_virtual_link(iface)
-        if retcode != 0:
-            self._print_stdout("/etc/network/if-pre-up.d/ifenslave: line 39: /sys/class/net/"
-                               f"{iface}/bonding/miimon: No such file or directory")
-            self._print_stdout("/etc/network/if-pre-up.d/ifenslave: line 39: /sys/class/net/"
-                               f"{iface}/bonding/mode: No such file or directory")
-        link["slaves"] = config["slaves"]
-        for slave in config["slaves"]:
-            self._enslave_iface(slave, iface, retcode != 0)
-        self._set_link_state(iface, link, True)
-        return self._add_address(iface, config, link)
-
-    def _set_bonding_down(self, iface, config):
-        link, retcode = self._get_link(iface)
-        if retcode != 0:
-            return 0
-        self._remove_address(config, link)
-        self._set_link_state(iface, link, False)
-        for slave in config["slaves"]:
-            self._unenslave_iface(slave)
-        self._remove_virtual_link(iface)
-        return 0
-
-    def _set_vlan_up(self, iface, config):
-        raw_dev = config["raw_dev"]
-        if config["add_link_cmd"]:
-            link, retcode = self._get_link(raw_dev)
-            if retcode != 0:
-                return retcode
-        else:
-            if raw_dev not in self._links:
-                self._print_stdout(f'cat: /sys/class/net/{raw_dev}/mtu: No such file or directory')
-                self._print_stdout(f'Device "{raw_dev}" does not exist.')
-                self._print_stdout(f'{raw_dev} does not exist, unable to create {iface}')
-                self._print_stdout('run-parts: /etc/network/if-pre-up.d/vlan exited with '
-                                   'return code 1')
-                return 1
-        link, retcode = self._create_virtual_link(iface)
-        if retcode != 0:
-            return 1
-        link["raw_dev"] = raw_dev
-        link["vlan_id"] = config["vlan_id"]
-        deps = self._links[raw_dev].setdefault("deps", list())
-        deps.append(iface)
-        self._set_link_state(iface, link, True)
-        return self._add_address(iface, config, link)
-
-    def _set_vlan_down(self, iface, config):
-        link, retcode = self._get_link(iface)
-        if retcode != 0:
-            return 0
-        self._remove_address(config, link)
-        self._set_link_state(iface, link, False)
-        self._remove_virtual_link(iface)
-        return 0
-
-    def _set_label_up(self, iface, config):  # pylint: disable=unused-argument
-        parent = config["parent"]
-        link, retcode = self._get_link(parent)
-        if retcode != 0:
-            return retcode
-        return self._add_address(parent, config, link)
-
-    def _set_label_down(self, iface, config):  # pylint: disable=unused-argument
-        parent = config["parent"]
-        link, retcode = self._get_link(parent)
-        if retcode == 0:
-            self._remove_address(config, link)
-        return 0
-
-    def _set_ifstate(self, iface, state):
-        path = anc.IFSTATE_BASE_PATH + iface
-        contents = iface if state else ''
-        self._fs.set_file_contents(path, contents)
-
-    _UP_FUNCTIONS = {anc.LO: _set_lo_up,
-                     anc.ETH: _set_eth_up,
-                     anc.SLAVE: _set_slave_up,
-                     anc.BONDING: _set_bonding_up,
-                     anc.VLAN: _set_vlan_up,
-                     anc.LABEL: _set_label_up}
-
-    _DOWN_FUNCTIONS = {anc.LO: _set_lo_down,
-                       anc.ETH: _set_eth_down,
-                       anc.SLAVE: _set_slave_down,
-                       anc.BONDING: _set_bonding_down,
-                       anc.VLAN: _set_vlan_down,
-                       anc.LABEL: _set_label_down}
-
-    def _get_iface_config(self, iface):
-        self._update_config()
-        if not (config := self._current_config["ifaces"].get(iface, None)):
-            self._print_stdout(f"ifup: unknown interface {iface}")
-            return None, 1
-        return config, 0
-
-    def _run_command(self, fxn, *args, **kwargs):
-        self._reset_stdout()
-        retcode = fxn(*args, **kwargs)
-        return retcode, self._stdout
-
-    def _do_ifup(self, iface):
-        config, retcode = self._get_iface_config(iface)
-        if retcode != 0:
-            return 1
-        if self._is_up(iface):
-            self._print_stdout(f"ifup: interface {iface} already configured")
-            return 0
-        fxn = self._UP_FUNCTIONS[config["type"]]
-        retcode = fxn(self, iface, config)
-        if retcode == 0:
-            self._set_ifstate(iface, True)
-        else:
-            self._print_stdout(f"ifup: failed to bring up {iface}")
-        return retcode
-
-    def ifup(self, iface):
-        self._add_history("ifup", iface)
-        return self._run_command(self._do_ifup, iface)
-
-    def _do_ifdown(self, iface):
-        config, retcode = self._get_iface_config(iface)
-        if retcode != 0:
-            return 1
-        if not self._is_up(iface):
-            self._print_stdout(f"ifdown: interface {iface} not configured")
-            return 0
-        fxn = self._DOWN_FUNCTIONS[config["type"]]
-        retcode = fxn(self, iface, config)
-        if retcode == 0:
-            self._set_ifstate(iface, False)
-        return retcode
-
-    def ifdown(self, iface):
-        self._add_history("ifdown", iface)
-        return self._run_command(self._do_ifdown, iface)
-
-    def ip_addr_show(self):
-        self._add_history("ip_addr_show")
-        return 0, "< 'ip addr show' output placeholder >\n"
-
-    def _do_ip_addr_show_dev(self, iface):
-        link, retcode = self._get_link_for_ip_cmd(iface)
-        if retcode != 0:
-            return retcode
-        name = iface
-        if raw_dev := link.get("raw_dev", None):
-            name += "@" + raw_dev
-        state = "UP" if link["adm_state"] else "DOWN"
-        addresses = [str(addr) for addr in sorted(list(link["addresses"]))]
-        text = f"{name:<16} {state:<14} {' '.join(addresses)}"
-        self._print_stdout(text)
-        return 0
-
-    def ip_addr_show_dev(self, iface):
-        self._add_history("ip_addr_show_dev", iface)
-        return self._run_command(self._do_ip_addr_show_dev, iface)
-
-    def _do_ip_addr_add(self, addr, iface):
-        link, retcode = self._get_link_for_ip_cmd(iface)
-        if retcode != 0:
-            return retcode
-        try:
-            ip = IPNetwork(addr)
-        except AddrFormatError:
-            self._print_stdout(f'Error: any valid prefix is expected rather than "{addr}".')
-            return 1
-        if ip in link["addresses"]:
-            self._print_stdout(f"Error: ipv{ip.version}: Address already assigned.")
-            return 1
-        link["addresses"].add(ip)
-        return 0
-
-    def ip_addr_add(self, addr, iface):
-        self._add_history("ip_addr_add", addr, iface)
-        return self._run_command(self._do_ip_addr_add, addr, iface)
-
-    def _do_ip_addr_flush(self, iface):
-        link, retcode = self._get_link_for_ip_cmd(iface)
-        if retcode != 0:
-            return retcode
-        for address in link["addresses"]:
-            self._remove_routes_associated_to_address(link, address)
-        link["addresses"].clear()
-        return 0
-
-    def ip_addr_flush(self, iface):
-        self._add_history("ip_addr_flush", iface)
-        return self._run_command(self._do_ip_addr_flush, iface)
-
-    def _do_ip_link_set_updown(self, iface, state):
-        link, retcode = self._get_link_for_ip_cmd(iface)
-        if retcode != 0:
-            return retcode
-        self._set_link_state(iface, link, state)
-        return 0
-
-    def ip_link_set_down(self, iface):
-        self._add_history("ip_link_set_down", iface)
-        return self._run_command(self._do_ip_link_set_updown, iface, False)
-
-    def ip_link_set_up(self, iface):
-        self._add_history("ip_link_set_up", iface)
-        return self._run_command(self._do_ip_link_set_updown, iface, True)
-
-    def ip_route_show_all(self, prot):
-        self._add_history("ip_route_show_all", prot)
-        return 0, "< 'ip route show all' output placeholder >\n"
-
-    @staticmethod
-    def _sort_routes(routes):
-        return [routes[k] for k in sorted(routes.keys())]
-
-    def _print_route(self, route, route_filter=None):
-        net = route["net"]
-        if net.value == 0 and net.prefixlen == 0:
-            pieces = ["default"]
-        else:
-            pieces = [f"{net.ip}/{net.prefixlen}"]
-        if not route_filter or not route_filter["via"]:
-            pieces.append(f'via {route["via"]}')
-        if not route_filter or not route_filter["dev"]:
-            pieces.append(f'dev {route["dev"]}')
-        if not route_filter or not route_filter["metric"]:
-            if (metric := route["metric"]) != 0 or net.version != 4:
-                pieces.append(f'metric {metric}')
-        if net.version == 6:
-            pieces.append("pref medium")
-        self._print_stdout(" ".join(pieces))
-
-    def _do_ip_route_show(self, prot, network, gateway, dev, metric):
-        # pylint: disable=too-many-arguments
-        ip_version = 6 if prot == "-6" else 4
-        filter_filter = self._get_route_filter(network, gateway, dev, metric, ip_version)
-        routes = self._find_routes(filter_filter)
-        for route in self._sort_routes(routes):
-            self._print_route(route, filter_filter)
-        return 0
-
-    def ip_route_show(self, prot, network, gateway, dev, metric):
-        # pylint: disable=too-many-arguments
-        self._add_history("ip_route_show", prot, network, gateway, dev, metric)
-        return self._run_command(self._do_ip_route_show, prot, network, gateway, dev, metric)
-
-    @staticmethod
-    def _get_route_obj(network, gateway, dev, metric):
-        gateway_ip = IPAddress(gateway)
-        if network == "default":
-            net = IPNetwork('0.0.0.0/0') if gateway_ip.version == 4 else IPNetwork('::0/0')
-        else:
-            net = IPNetwork(network)
-        if metric:
-            metric_val = int(metric)
-            if gateway_ip.version == 6 and metric_val == 0:
-                metric_val = 1024
-        else:
-            metric_val = 0 if gateway_ip.version == 4 else 1024
-        return {"net": net, "via": gateway_ip, "dev": dev, "metric": metric_val}
-
-    def _add_route(self, route_obj, link):
-        route_id = self._next_route_id
-        self._next_route_id += 1
-        self._routes[route_id] = route_obj
-        link["routes"].add(route_id)
-
-    def _remove_route(self, route_id):
-        route_obj = self._routes.pop(route_id)
-        self._links[route_obj["dev"]]["routes"].remove(route_id)
-
-    @staticmethod
-    def _get_route_filter(network, gateway, dev, metric, version=None):
-        gateway_ip = IPAddress(gateway) if gateway else None
-        if network == "default":
-            if (version and version == 6) or (gateway_ip and gateway_ip.version == 6):
-                net = IPNetwork('::0/0')
-            else:
-                net = IPNetwork('0.0.0.0/0')
-        else:
-            net = IPNetwork(network) if network else None
-        metric_val = int(metric) if metric else None
-        return {"net": net, "via": gateway_ip, "dev": dev,
-                "metric": metric_val, "version": version}
-
-    @staticmethod
-    def _route_matches(route_filter, route):
-        filter_net = route_filter["net"]
-        route_net = route["net"]
-        version = route_filter["version"]
-        if version and route_net.version != version:
-            return False
-        if route_net != filter_net:
-            return False
-        for prop in ["via", "dev", "metric"]:
-            if not (val := route_filter[prop]):
-                continue
-            if route[prop] != val:
-                return False
-        return True
-
-    def _find_routes(self, route_filter, single=False):
-        routes = dict()
-        for route_id, route in self._routes.items():
-            if self._route_matches(route_filter, route):
-                routes[route_id] = route
-                if single:
-                    break
-        return routes
-
-    def _route_exists(self, network, gateway, dev, metric):
-        route_filter = self._get_route_filter(network, gateway, dev, metric)
-        return bool(self._find_routes(route_filter, True))
-
-    def _erase_routes_by_filter(self, route_filter):
-        to_remove = []
-        for route_id, route in self._routes.items():
-            if self._route_matches(route_filter, route):
-                to_remove.append(route_id)
-        for route_id in to_remove:
-            self._remove_route(route_id)
-
-    def _check_can_add_route(self, route_obj, link):
-        gateway = route_obj["via"]
-        for addr in link["addresses"]:
-            if gateway in addr:
-                return 0
-        self._print_stdout("RTNETLINK answers: No route to host")
-        return 2
-
-    def _do_ip_route_add(self, network, gateway, dev, metric):
-        link, retcode = self._get_link(dev)
-        if retcode != 0:
-            return retcode
-        if self._route_exists(network, gateway, dev, metric):
-            self._print_stdout("RTNETLINK answers: File exists")
-            return 2
-        route_obj = self._get_route_obj(network, gateway, dev, metric)
-        retcode = self._check_can_add_route(route_obj, link)
-        if retcode != 0:
-            return retcode
-        self._add_route(route_obj, link)
-        return 0
-
-    def ip_route_add(self, network, gateway, dev, metric):
-        self._add_history("ip_route_add", network, gateway, dev, metric)
-        return self._run_command(self._do_ip_route_add, network, gateway, dev, metric)
-
-    def _do_ip_route_replace(self, network, gateway, dev, metric):
-        link, retcode = self._get_link(dev)
-        if retcode != 0:
-            return retcode
-        ip_version = IPAddress(gateway).version
-        route_obj = self._get_route_obj(network, gateway, dev, metric)
-        retcode = self._check_can_add_route(route_obj, link)
-        if retcode != 0:
-            return retcode
-        route_filter = self._get_route_filter(network, None, None, metric, ip_version)
-        self._erase_routes_by_filter(route_filter)
-        self._add_route(route_obj, link)
-        return 0
-
-    def ip_route_replace(self, network, gateway, dev, metric):
-        self._add_history("ip_route_replace", network, gateway, dev, metric)
-        return self._run_command(self._do_ip_route_replace, network, gateway, dev, metric)
-
-    def _do_ip_route_del(self, network, gateway, dev, metric):
-        route_filter = self._get_route_filter(network, gateway, dev, metric)
-        routes = self._find_routes(route_filter)
-        if len(routes) == 0:
-            self._print_stdout("RTNETLINK answers: No such process")
-            return 2
-        for route_id in routes.keys():
-            self._remove_route(route_id)
-        return 0
-
-    def ip_route_del(self, network, gateway, dev, metric):
-        self._add_history("ip_route_del", network, gateway, dev, metric)
-        return self._run_command(self._do_ip_route_del, network, gateway, dev, metric)
-
-    @staticmethod
-    def _get_link_text(link):
-        pieces = ["UP" if link["adm_state"] else "DOWN"]
-        if raw_dev := link.get("raw_dev", None):
-            pieces.append(f"VLAN({raw_dev},{link['vlan_id']})")
-        elif master := link.get("master", None):
-            pieces.append(f"SLAVE({master})")
-        elif slaves := link.get("slaves", None):
-            pieces.append(f"BONDING({','.join(slaves)})")
-        pieces.extend([str(ip) for ip in sorted(link["addresses"])])
-        return " ".join(pieces)
-
-    def get_link_status(self, name):
-        if not (link := self._links.get(name, None)):
-            raise NetworkingMockError(f"Link does not exist: '{name}'")
-        return self._get_link_text(link)
-
-    def get_links_status(self):
-        return [name + " " + self._get_link_text(self._links[name])
-                for name in sorted(self._links.keys())]
-
-    @staticmethod
-    def _get_route_text(route):
-        net = route["net"]
-        net_text = "default" if net.value == 0 and net.prefixlen == 0 else str(net)
-        text = f"{net_text} via {route['via']} dev {route['dev']}"
-        if metric := route["metric"]:
-            text += f" metric {metric}"
-        return text
-
-    def get_routes(self):
-        return [self._get_route_text(self._routes[id]) for id in sorted(self._routes.keys())]
-
-    def set_allow_multiple_default_gateways(self, allow: bool):
-        self._allow_multiple_default_gateways = allow
-
-
-class SystemCommandMockError(BaseException):
-    pass
-
-
-class SystemCommandMock():  # pylint: disable=too-few-public-methods
-    def __init__(self, nwmock: NetworkingMock):
-        self._nwmock = nwmock
-
-    def _ip_addr_show(self, _):
-        return self._nwmock.ip_addr_show()
-
-    def _ip_br_addr_show_dev(self, args):
-        return self._nwmock.ip_addr_show_dev(args[0])
-
-    def _ip_addr_add(self, args):
-        return self._nwmock.ip_addr_add(args[0], args[1])
-
-    def _ip_addr_flush(self, args):
-        return self._nwmock.ip_addr_flush(args[0])
-
-    def _ip_link_set_down(self, args):
-        return self._nwmock.ip_link_set_down(args[0])
-
-    def _ip_route_show_all(self, args):
-        return self._nwmock.ip_route_show_all(args[0])
-
-    def _ip_route_show(self, args):
-        return self._nwmock.ip_route_show(args[0], args[1], args[2], args[3], args[4])
-
-    def _ip_route_add(self, args):
-        return self._nwmock.ip_route_add(args[0], args[1], args[2], args[3])
-
-    def _ip_route_replace(self, args):
-        return self._nwmock.ip_route_replace(args[0], args[1], args[2], args[3])
-
-    def _ip_route_del(self, args):
-        return self._nwmock.ip_route_del(args[0], args[1], args[2], args[3])
-
-    def _ifup(self, args):
-        return self._nwmock.ifup(args[0])
-
-    def _ifdown(self, args):
-        return self._nwmock.ifdown(args[0])
-
-    _MAPPINGS = (
-        (re.compile(R"^/sbin/ifup (?:-v )?(\S+)$"), _ifup),
-        (re.compile(R"^/sbin/ifdown (?:-v )?(\S+)$"), _ifdown),
-        (re.compile(R"^/usr/sbin/ip addr show$"), _ip_addr_show),
-        (re.compile(R"^/usr/sbin/ip -br addr show dev (\S+)$"), _ip_br_addr_show_dev),
-        (re.compile(R"^/usr/sbin/ip addr add (\S+) dev (\S+)$"), _ip_addr_add),
-        (re.compile(R"^/usr/sbin/ip addr flush dev (\S+)$"), _ip_addr_flush),
-        (re.compile(R"^/usr/sbin/ip link set down dev (\S+)$"), _ip_link_set_down),
-        (re.compile(R"^/usr/sbin/ip (?:(-6) )?route show$"), _ip_route_show_all),
-        (re.compile(R"^/usr/sbin/ip (?:(-6) )?route show (\S+)(?: via (\S+) "
-                    R"dev (\S+))?(?: metric (\S+))?$"), _ip_route_show),
-        (re.compile(R"^/usr/sbin/ip route add (\S+) via (\S+) "
-                    R"dev (\S+)(?: metric (\S+))?$"), _ip_route_add),
-        (re.compile(R"^/usr/sbin/ip route replace (\S+) via (\S+) "
-                    R"dev (\S+)(?: metric (\S+))?$"), _ip_route_replace),
-        (re.compile(R"^/usr/sbin/ip route del (\S+) via (\S+) "
-                    R"dev (\S+)(?: metric (\S+))?$"), _ip_route_del),)
-
-    def execute_system_cmd(self, cmd):
-        for mapping in self._MAPPINGS:
-            if result := mapping[0].search(cmd):
-                return mapping[1](self, result.groups())
-        raise SystemCommandMockError(f"Unrecognized command: '{cmd}'")
-
-
-class LoggerMock():
-    DEBUG = "debug"
-    INFO = "info"
-    WARNING = "warning"
-    ERROR = "error"
-    FATAL = "fatal"
-
-    def __init__(self):
-        self._entries = list()
-
-    def _log(self, log_type, msg):
-        self._entries.append((log_type, msg))
-
-    def get_history(self):
-        return self._entries
-
-    def reset_history(self):
-        self._entries.clear()
-
-    def basicConfig(self):
-        pass
-
-    def debug(self, msg):
-        self._log(self.DEBUG, msg)
-
-    def info(self, msg):
-        self._log(self.INFO, msg)
-
-    def warning(self, msg):
-        self._log(self.WARNING, msg)
-
-    def error(self, msg):
-        self._log(self.ERROR, msg)
-
-    def fatal(self, msg):
-        self._log(self.FATAL, msg)
-
-
-class ConfigFileGenerator():
-    _SHORT_HEADER = ["# HEADER: Last generated at: 2025-01-01 00:00:00 +0000"]
-
-    _LONG_HEADER = ["# HEADER: This file is being managed by puppet. Changes to",
-                    "# HEADER: interfaces that are not being managed by puppet will persist;",
-                    "# HEADER: however changes to interfaces that are being managed by puppet will",
-                    "# HEADER: be overwritten. In addition, file order is NOT guaranteed.",
-                    "# HEADER: Last generated at: 2025-01-01 00:00:00 +0000", "", ""]
-
-    _AUTOCFG = ("echo 0 > /proc/sys/net/ipv6/conf/{ifname}/autoconf; "
-                "echo 0 > /proc/sys/net/ipv6/conf/{ifname}/accept_ra; "
-                "echo 0 > /proc/sys/net/ipv6/conf/{ifname}/accept_redirects")
-
-    _TEMPLATE = {
-        "iface": "iface {ifname} {inet} {mode}",
-        "vlan-raw-device": "vlan-raw-device {raw_dev}",
-        "address": "address {address}",
-        "netmask": "netmask {netmask}",
-        "gateway": "{indent}gateway {gateway}",
-        "bond-master": "{indent}bond-master {master}",
-        "bond-miimon": "{indent}bond-miimon 100",
-        "bond-mode": "{indent}bond-mode active-backup",
-        "bond-primary": "{indent}bond-primary {primary}",
-        "bond-slaves": "{indent}bond-slaves {slaves}",
-        "hwaddress": "{indent}hwaddress {hwaddress}",
-        "mtu": "{indent}mtu {mtu}",
-        "pre-up-slave": "{indent}pre-up /usr/sbin/ip link set dev {device} promisc on; " + _AUTOCFG,
-        "pre-up-vlan-ifupdown": "{indent}pre-up /sbin/modprobe -q 8021q",
-        "pre-up-vlan-manual": "{indent}pre-up /sbin/modprobe -q 8021q; "
-                              "ip link add link {raw_dev} name {device} type vlan id {vlan_id}",
-        "up": "{indent}up sleep 10",
-        "post-up": "{indent}post-up " + _AUTOCFG,
-        "post-up-lo": "{indent}post-up /usr/local/bin/tc_setup.sh "
-                      "lo mgmt 10000 > /dev/null; " + _AUTOCFG,
-        "post-up-vlan": "{indent}post-up /usr/sbin/ip link set dev {device} mtu {mtu}; " + _AUTOCFG,
-        "post-down": "{indent}post-down ip link del {device}",
-        "scope": "{indent}scope host",
-        "stx-description": "{indent}stx-description ifname:{device},net:None",
-        "allow-": "{indent}allow-{master} {device}",
-    }
-
-    _PROPERTY_MAP = {
-        "lo": ["mtu", "post-up-lo", "scope", "stx-description"],
-        "eth": ["mtu", "post-up", "stx-description"],
-        "slave": ["bond-master", "mtu", "pre-up-slave", "stx-description", "allow-"],
-        "bonding": ["bond-miimon", "bond-mode", "bond-primary", "bond-slaves", "hwaddress",
-                    "mtu", "post-up", "stx-description", "up"],
-        "vlan-NNN": ["vlan-raw-device", "mtu", "post-up-vlan", "pre-up-vlan-ifupdown",
-                     "stx-description"],
-        "vlan-dot": ["mtu", "post-up-vlan", "pre-up-vlan-ifupdown", "stx-description"],
-        "vlan-manual": ["mtu", "post-down", "post-up-vlan", "pre-up-vlan-manual",
-                        "stx-description"],
-    }
-
-    def __init__(self):
-        self._hwaddr_seq = 1
-
-    @staticmethod
-    def _get_basic_props(config):
-        props = ["iface"]
-        if config.get("address", None):
-            props.append("address")
-            props.append("netmask")
-            if config.get("gateway", None):
-                props.append("gateway")
-        return props
-
-    def _get_props(self, config):
-        return self._get_basic_props(config) + self._PROPERTY_MAP[config["type"]]
-
-    def _gen_cfg_lines(self, config):
-        props = self._get_props(config)
-        lines = list()
-        for prop in props:
-            lines.append(self._TEMPLATE[prop].format(**config))
-        return lines
-
-    def _get_new_hw_address(self):
-        hwaddr = f"08:00:27:f2:66:{self._hwaddr_seq:02d}"
-        self._hwaddr_seq += 1
-        return hwaddr
-
-    def _parse_cfg(self, ifname, input_config, indent=False):
-        config = input_config.copy()
-        if ifname == "lo":
-            iftype = "lo"
-        elif res := re.search(R"^(\S+)\.(\d+)(?:\:\S+)?$", ifname):
-            iftype = "vlan-dot"
-            config["raw_dev"] = res.groups()[0]
-            config["vlan_id"] = res.groups()[1]
-        elif res := re.search(R"^vlan(\d+)(?:\:\S+)?$", ifname):
-            iftype = "vlan-NNN"
-            config["vlan_id"] = res.groups()[0]
-        elif "vlan_id" in input_config:
-            iftype = "vlan-manual"
-        elif "master" in input_config:
-            iftype = "slave"
-        elif slaves := input_config.get("slaves", None):
-            iftype = "bonding"
-            config["slaves"] = " ".join(slaves)
-            config["primary"] = slaves[0]
-            if "hwaddress" not in input_config:
-                config["hwaddress"] = self._get_new_hw_address()
-        else:
-            iftype = "eth"
-        config["type"] = iftype
-        config["ifname"] = ifname
-        config["device"] = ifname.split(":")[0] if ":" in ifname else ifname
-        config["mtu"] = input_config.get("mtu", 1500)
-        config["indent"] = "    " if indent else ""
-
-        if address := input_config.get("address", None):
-            net = IPNetwork(address)
-            config["address"] = str(net.ip)
-            config["netmask"] = str(net.netmask) if net.version == 4 else net.prefixlen
-            config["inet"] = "inet6" if net.version == 6 else "inet"
-            config["mode"] = "static"
-        else:
-            config["inet"] = input_config.get("inet", "inet")
-            config["mode"] = input_config.get("mode", "manual")
-
-        return config
-
-    @staticmethod
-    def _gen_auto_lines(auto):
-        return ["auto " + " ".join(auto)]
-
-    @staticmethod
-    def _gen_route_line(route):
-        net = IPNetwork(route["net"])
-        line = f"{net.ip} {net.netmask} {route['via']} {route['dev']}"
-        if metric := route.get("metric", None):
-            line += f" metric {metric}"
-        return line
-
-    def _gen_routes_lines(self, routes):
-        return [self._gen_route_line(route) for route in routes]
-
-    def generate_auto_file(self, auto):
-        lines = self._gen_auto_lines(auto)
-        return '\n'.join(lines) + '\n'
-
-    def generate_ifcfg_file(self, ifname, config):
-        config = self._parse_cfg(ifname, config)
-        lines = self._SHORT_HEADER + self._gen_cfg_lines(config)
-        return '\n'.join(lines) + '\n'
-
-    def generate_interfaces_file(self, config):
-        lines = self._LONG_HEADER.copy()
-        for ifname, input_cfg in config.items():
-            if ifname == "auto":
-                lines.extend(self._gen_auto_lines(input_cfg))
-            else:
-                output_cfg = self._parse_cfg(ifname, input_cfg, True)
-                lines.extend(self._gen_cfg_lines(output_cfg))
-            lines.append('')
-        return '\n'.join(lines) + '\n'
-
-    def generate_routes_file(self, routes):
-        lines = self._LONG_HEADER + self._gen_routes_lines(routes)
-        return '\n'.join(lines) + '\n'
-
-    def _generate_ifcfg_files(self, tree, contents):
-        for name, config in contents.items():
-            if name == "auto":
-                tree[anc.ETC_DIR + "/auto"] = self.generate_auto_file(config)
-            else:
-                tree[anc.ETC_DIR + "/ifcfg-" + name] = self.generate_ifcfg_file(name, config)
-
-    def generate_file_tree(self, puppet_files=None, etc_files=None):
-        tree = dict()
-
-        if puppet_files:
-            if interfaces := puppet_files.get("interfaces", None):
-                tree[anc.PUPPET_FILE] = self.generate_interfaces_file(interfaces)
-            if routes := puppet_files.get("routes", None):
-                tree[anc.PUPPET_ROUTES_FILE] = self.generate_routes_file(routes)
-            if routes6 := puppet_files.get("routes6", None):
-                tree[anc.PUPPET_ROUTES6_FILE] = self.generate_routes_file(routes6)
-
-        if etc_files:
-            if interfaces := etc_files.get("interfaces", None):
-                self._generate_ifcfg_files(tree, interfaces)
-            routes = etc_files.get("routes", [])
-            routes6 = etc_files.get("routes6", [])
-            if routes or routes6:
-                tree[anc.ETC_ROUTES_FILE] = self.generate_routes_file(routes + routes6)
-
-        return tree
-
-
-FILE_GEN = ConfigFileGenerator()
-
-
-class BaseTestCase(testtools.TestCase):
-    def tearDown(self):
-        self._log = None
-        self._scmdmock = None
-        self._nwmock = None
-        self._fs = None
-        return super().tearDown()
-
-    def _add_fs_mock(self, contents=None):
-        self._fs = FilesystemMock(contents)
-
-    def _add_logger_mock(self):
-        self._log = LoggerMock()
-
-    def _add_nw_mock(self, static_links):
-        self._nwmock = NetworkingMock(self._fs, static_links)
-
-    def _add_scmd_mock(self):
-        self._scmdmock = SystemCommandMock(self._nwmock)
-
-    def _mock_fs(self, mocks, fxn, *args, **kwargs):
-        with (
-            mock.patch("src.bin.apply_network_config.path_exists", self._fs.exists),
-            mock.patch("os.remove", self._fs.delete),
-            mock.patch("builtins.open", self._fs.open),
-            mock.patch.multiple("os.path",
-                                isfile=self._fs.isfile,
-                                isdir=self._fs.isdir,
-                                islink=self._fs.islink)
-        ):
-            return self._mocked_call(mocks, fxn, *args, **kwargs)
-
-    def _mock_logger(self, mocks, fxn, *args, **kwargs):
-        with mock.patch.multiple("logging",
-                                 basicConfig=self._log.basicConfig,
-                                 debug=self._log.debug,
-                                 info=self._log.info,
-                                 warning=self._log.warning,
-                                 error=self._log.error,
-                                 fatal=self._log.fatal):
-            return self._mocked_call(mocks, fxn, *args, **kwargs)
-
-    def _mock_syscmd(self, mocks, fxn, *args, **kwargs):
-        with mock.patch("src.bin.apply_network_config.execute_system_cmd",
-                        self._scmdmock.execute_system_cmd):
-            return self._mocked_call(mocks, fxn, *args, **kwargs)
-
-    def _mock_sysinv_lock(self, mocks, fxn, *args, **kwargs):
-        with mock.patch.multiple("src.bin.apply_network_config",
-                                 acquire_sysinv_agent_lock=mock.DEFAULT,
-                                 release_sysinv_agent_lock=mock.DEFAULT):
-            return self._mocked_call(mocks, fxn, *args, **kwargs)
-
-    @staticmethod
-    def _mocked_call(mocks, fxn, *args, **kwargs):
-        if len(mocks) == 0:
-            return fxn(*args, **kwargs)
-        return mocks[0](mocks[1:], fxn, *args, **kwargs)
-
-
-class GeneralTests(BaseTestCase):  # pylint: disable=too-many-public-methods
-    def test_stanza_parser(self):
-        parser = anc.StanzaParser()
-        parser.parse_lines([
-            "# HEADER: Last generated at: 2024-11-06 00:54:24 +0000",
-            "iface   enp0s3\tinet manual   ",
-            "# Comment",
-            "    \t  # Comment",
-            "",
-            "mtu      1500",
-            "\tpost-up echo 0 > /proc/sys/net/ipv6/conf/enp0s3/autoconf       ",
-            "    stx-description ifname:oam0,net:None",
-            ""])
-        parser.parse_lines([
-            "# HEADER: Last generated at: 2024-11-06 00:54:24 +0000",
-            "auto\tlo\tenp0s3       vlan200      ",
-            "iface vlan200 inet manual",
-            "vlan-raw-device enp0s3",
-            "    mtu 1500",
-            "    post-up /usr/sbin/ip link set dev vlan200 mtu 1500",
-            "    pre-up /sbin/modprobe -q 8021q",
-            "    stx-description ifname:vlan200,net:None",
-            "iface   ",
-            "    address 10.23.44.11",
-            "    netmask 255.255.255.0",
-            "    mtu 1500",
-            "iface enp0s8 inet manual",
-            "    mtu 1500",
-            "    post-up echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf",
-            "    stx-description ifname:etc0,net:None",
-            ""])
-        parser.parse_lines(["    auto   "])
-        parser.parse_lines(["    auto lo enp0s3 enp0s8"])
-        parser.parse_lines(["\tauto  \t  lo \t enp0s8 enp0s9"])
-
-        auto, ifaces = parser.get_auto_and_ifaces()
-        self.assertEqual(["lo", "enp0s3", "vlan200", "enp0s8", "enp0s9"], auto)
-        self.assertEqual({
-            'enp0s3': {
-                'iface': 'enp0s3 inet manual',
-                'mtu': '1500',
-                'post-up': 'echo 0 > /proc/sys/net/ipv6/conf/enp0s3/autoconf',
-                'stx-description': 'ifname:oam0,net:None'},
-            'vlan200': {
-                'iface': 'vlan200 inet manual',
-                'mtu': '1500',
-                'post-up': '/usr/sbin/ip link set dev vlan200 mtu 1500',
-                'pre-up': '/sbin/modprobe -q 8021q',
-                'stx-description': 'ifname:vlan200,net:None',
-                'vlan-raw-device': 'enp0s3'},
-            'enp0s8': {
-                'iface': 'enp0s8 inet manual',
-                'mtu': '1500',
-                'post-up': 'echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf',
-                'stx-description': 'ifname:etc0,net:None'}},
-            ifaces)
-
-    def test_is_label(self):
-        self.assertEqual(True, anc.is_label("enp0s8:2-7"))
-        self.assertEqual(False, anc.is_label("enp0s8"))
-
-    def test_get_base_iface(self):
-        self.assertEqual("enp0s8", anc.get_base_iface("enp0s8:2-7"))
-        self.assertEqual("vlan-200", anc.get_base_iface("vlan-200:11"))
-
-    def test_read_file_lines(self):
-        self._add_fs_mock({"/test-dir/test-file": "0\n1\n2\n"})
-        lines = self._mocked_call([self._mock_fs], anc.read_file_lines, "/test-dir/test-file")
-        self.assertEqual(3, len(lines))
-        self.assertEqual("0", lines[0])
-        self.assertEqual("1", lines[1])
-        self.assertEqual("2", lines[2])
-
-    _HEADER = "# HEADER: Last generated at: 2025-01-01 00:00:00 +0000"
-
-    _IFACE_CONFIG = {"iface": "enp0s8 inet static",
-                     "mtu": "9000",
-                     "address": "12.12.1.55",
-                     "netmask": "255.255.255.0",
-                     "post-up": "echo # > /proc/sys/net/ipv6/conf/enp0s8/autoconf",
-                     "stx-description": "ifname:etc0,net:None"}
-
-    _IFACE_FILE = (f"{_HEADER}\n"
-                    "iface enp0s8 inet static\n"
-                    "address 12.12.1.55\n"
-                    "netmask 255.255.255.0\n"
-                    "mtu 9000\n"
-                    "post-up echo # > /proc/sys/net/ipv6/conf/enp0s8/autoconf\n"
-                    "stx-description ifname:etc0,net:None\n")
-
-    def test_parse_valid_ifcfg_file(self):
-        self._add_fs_mock({anc.ETC_DIR + "/ifcfg-enp0s8": self._IFACE_FILE})
-        config = self._mocked_call([self._mock_fs], anc.parse_ifcfg_file, "enp0s8")
-        self.assertEqual(6, len(config))
-        self.assertEqual("enp0s8 inet static", config["iface"])
-        self.assertEqual("12.12.1.55", config["address"])
-        self.assertEqual("255.255.255.0", config["netmask"])
-        self.assertEqual("9000", config["mtu"])
-        self.assertEqual("echo # > /proc/sys/net/ipv6/conf/enp0s8/autoconf", config["post-up"])
-        self.assertEqual("ifname:etc0,net:None", config["stx-description"])
-
-    def test_parse_missing_ifcfg_file(self):
-        self._add_fs_mock()
-        self._add_logger_mock()
-        config = self._mocked_call([self._mock_fs, self._mock_logger],
-                                   anc.parse_ifcfg_file, "enp0s8")
-        self.assertEqual(0, len(config))
-        self.assertEqual(LoggerMock.WARNING, self._log.get_history()[-1][0])
-        self.assertEqual(f"Interface config file not found: '{anc.ETC_DIR + '/ifcfg-enp0s8'}'",
-                         self._log.get_history()[-1][1])
-
-    def test_parse_ifcfg_file_with_multiple_config(self):
-        path = anc.ETC_DIR + "/ifcfg-enp0s8"
-        self._add_fs_mock({path: self._IFACE_FILE +
-                           "iface enp0s9 inet static\n"
-                           "mtu 9000\n"
-                           "stx-description ifname:etc1,net:None\n"})
-        self._add_logger_mock()
-        config = self._mocked_call([self._mock_fs, self._mock_logger],
-                                   anc.parse_ifcfg_file, "enp0s8")
-        self.assertEqual(6, len(config))
-        self.assertEqual(LoggerMock.WARNING, self._log.get_history()[-1][0])
-        self.assertEqual(f"Multiple interface configs found in '{path}': enp0s8 enp0s9",
-                         self._log.get_history()[-1][1])
-
-    def test_parse_invalid_ifcfg_file(self):
-        path = anc.ETC_DIR + "/ifcfg-enp0s8"
-        self._add_fs_mock({path: "invalid content line 1\n"
-                                 "invalid content line 2\n"
-                                 "invalid content line 3\n"})
-        self._add_logger_mock()
-        config = self._mocked_call([self._mock_fs, self._mock_logger],
-                                   anc.parse_ifcfg_file, "enp0s8")
-        self.assertEqual(0, len(config))
-        self.assertEqual(LoggerMock.WARNING, self._log.get_history()[-1][0])
-        self.assertEqual(f"No interface config found in '{path}'", self._log.get_history()[-1][1])
-
-    def test_parse_ifcfg_file_with_unrelated_ifaces(self):
-        path = anc.ETC_DIR + "/ifcfg-enp0s8"
-        self._add_fs_mock({path: "iface enp0s9 inet static\n"
-                                 "mtu 9000\n"
-                                 "stx-description ifname:etc1,net:None\n"
-                                 "iface enp0s10 inet static\n"
-                                 "mtu 9000\n"
-                                 "stx-description ifname:etc2,net:None\n"})
-        self._add_logger_mock()
-        config = self._mocked_call([self._mock_fs, self._mock_logger],
-                                   anc.parse_ifcfg_file, "enp0s8")
-        self.assertEqual(0, len(config))
-        self.assertEqual(LoggerMock.WARNING, self._log.get_history()[-1][0])
-        self.assertEqual(f"Config for interface 'enp0s8' not found in '{path}'. Instead, "
-                         f"file has config(s) for the following interface(s): enp0s10 enp0s9",
-                         self._log.get_history()[-1][1])
-
-    def test_parse_auto_file(self):
-        self._add_fs_mock({anc.ETC_DIR + "/auto":
-                           "auto  lo   enp0s3\tenp0s3:1-17 enp0s8 vlan100"})
-        auto = self._mocked_call([self._mock_fs], anc.parse_auto_file)
-        self.assertEqual(["lo", "enp0s3", "enp0s3:1-17", "enp0s8", "vlan100"], auto)
-
-    def test_parse_missing_auto_file(self):
-        self._add_fs_mock()
-        self._add_logger_mock()
-        auto = self._mocked_call([self._mock_fs, self._mock_logger], anc.parse_auto_file)
-        self.assertEqual(0, len(auto))
-        self.assertEqual(LoggerMock.INFO, self._log.get_history()[-1][0])
-        self.assertEqual(f"Auto file not found: '{anc.ETC_DIR + '/auto'}'",
-                         self._log.get_history()[-1][1])
-
-    def test_get_vlan_attributes_vlanNNN(self):
-        dev, vlan_id = anc.get_vlan_attributes("vlan123", {"vlan-raw-device": "enp0s8"})
-        self.assertEqual("enp0s8", dev)
-        self.assertEqual(123, vlan_id)
-
-    def test_get_vlan_attributes_vlanNNN_no_dev(self):
-        self._add_logger_mock()
-        attribs = self._mocked_call([self._mock_logger], anc.get_vlan_attributes,
-                                    "vlan123", {"iface": "vlan123 inet static"})
-        self.assertIsNone(attribs)
-        self.assertEqual(LoggerMock.WARNING, self._log.get_history()[-1][0])
-        self.assertEqual("vlan-raw-device property is empty or not specified for "
-                         "interface vlan123, so it will not be considered as a valid VLAN",
-                         self._log.get_history()[-1][1])
-
-    def test_get_vlan_attributes_vlan_dot(self):
-        dev, vlan_id = anc.get_vlan_attributes("enp0s8.123", {"iface": "enp0s8.123 inet static"})
-        self.assertEqual("enp0s8", dev)
-        self.assertEqual(123, vlan_id)
-
-    def test_get_vlan_attributes_vlan_manual(self):
-        dev, vlan_id = anc.get_vlan_attributes(
-            "data0",
-            {"pre-up": "/sbin/modprobe -q 8021q; "
-                       "/usr/sbin/ip  link   add link\tenp0s8 name data0 type vlan id 123"})
-        self.assertEqual("enp0s8", dev)
-        self.assertEqual(123, vlan_id)
-
-    def test_get_vlan_attributes_not_vlan(self):
-        attribs = anc.get_vlan_attributes("enp0s8", {"iface": "enp0s8 inet static"})
-        self.assertIsNone(attribs)
-
-    def test_get_types_and_dependencies(self):
-        iface_configs = {"bond0": {"bond-slaves": "enp0s9 enp0s10"},
-                         "bond0:0-16": {},
-                         "enp0s10": {"bond-master": "bond0"},
-                         "enp0s3": {},
-                         "enp0s3:3-7": {},
-                         "enp0s4": {},
-                         "enp0s4:5-17": {},
-                         "enp0s9": {"bond-master": "bond0"},
-                         "lo": {},
-                         "lo:1-2": {},
-                         "lo:5-14": {},
-                         "vlan200": {"vlan-raw-device": "bond0"},
-                         "vlan200:0-17": {}}
-
-        ifaces_types, dependencies = anc.get_types_and_dependencies(iface_configs)
-
-        self.assertEqual({
-            "bond0": "bonding",
-            "bond0:0-16": "label",
-            "enp0s10": "slave",
-            "enp0s3": "eth",
-            "enp0s3:3-7": "label",
-            "enp0s4": "eth",
-            "enp0s4:5-17": "label",
-            "enp0s9": "slave",
-            "lo": "lo",
-            "lo:1-2": "label",
-            "lo:5-14": "label",
-            "vlan200": "vlan",
-            "vlan200:0-17": "label"
-        }, ifaces_types)
-
-        self.assertEqual({
-            "bond0": {"vlan200", "bond0:0-16"},
-            "enp0s10": {"bond0"},
-            "enp0s3": {"enp0s3:3-7"},
-            "enp0s4": {"enp0s4:5-17"},
-            "enp0s9": {"bond0"},
-            "lo": {"lo:1-2", "lo:5-14"},
-            "vlan200": {"vlan200:0-17"}}, dependencies)
-
-    def test_is_iface_modified_true(self):
-        self._add_logger_mock()
-
-        current = {"iface": "enp0s8 inet manual",
-                   "mtu": "1500",
-                   "post-up": "echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf",
-                   "down": "ip addr flush dev enp0s8",
-                   "stx-description": "ifname:etc0,net:None"}
-
-        new = {"iface": "enp0s8 inet static",
-               "mtu": "9000",
-               "address": "12.12.1.55",
-               "netmask": "255.255.255.0",
-               "post-up": "echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf",
-               "stx-description": "ifname:data0,net:None"}
-
-        modified = self._mocked_call([self._mock_logger],
-                                     anc.is_iface_modified, "enp0s8", new, current)
-
-        self.assertEqual(True, modified)
-        self.assertEqual(LoggerMock.INFO, self._log.get_history()[-1][0])
-        self.assertEqual("Differences found for interface enp0s8:\n"
-                         "    Removed properties:\n"
-                         "        down ip addr flush dev enp0s8\n"
-                         "    Added properties:\n"
-                         "        address 12.12.1.55\n"
-                         "        netmask 255.255.255.0\n"
-                         "    Modified properties:\n"
-                         "        'iface' went from 'enp0s8 inet manual' to 'enp0s8 inet static'\n"
-                         "        'mtu' went from '1500' to '9000'",
-                         self._log.get_history()[-1][1])
-
-    def test_is_iface_modified_false(self):
-        current = {"iface": "enp0s8 inet manual",
-                   "mtu": "1500",
-                   "post-up": "echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf",
-                   "down": "ip addr flush dev enp0s8",
-                   "stx-description": "ifname:etc0,net:None",
-                   "random-property": "potato"}
-
-        new = {"iface": "enp0s8 inet manual",
-               "mtu": "1500",
-               "post-up": "echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf",
-               "down": "ip addr flush dev enp0s8",
-               "stx-description": "ifname:data0,net:None",
-               "random-property": "banana"}
-
-        modified = anc.is_iface_modified("enp0s8", new, current)
-
-        self.assertEqual(False, modified)
-
-    def test_get_dependent_list(self):
-        config = {"auto": {"lo", "lo:1-2", "lo:5-14", "enp0s3", "enp0s3:3-7", "enp0s4",
-                           "enp0s4:5-17", "enp0s9", "enp0s10", "bond0", "bond0:0-16",
-                           "vlan200", "vlan200:0-17"},
-                  "dependencies": {"bond0": {"vlan200", "bond0:0-16"},
-                                   "enp0s10": {"bond0"},
-                                   "enp0s3": {"enp0s3:3-7"},
-                                   "enp0s4": {"enp0s4:5-17"},
-                                   "enp0s9": {"bond0"},
-                                   "lo": {"lo:1-2", "lo:5-14"},
-                                   "vlan200": {"vlan200:0-17"}}}
-
-        dep1 = anc.get_dependent_list(config, {"vlan200"})
-        self.assertEqual({"vlan200", "vlan200:0-17"}, dep1)
-
-        dep2 = anc.get_dependent_list(config, {"bond0"})
-        self.assertEqual({"bond0", "bond0:0-16", "vlan200", "vlan200:0-17"}, dep2)
-
-        dep3 = anc.get_dependent_list(config, {"enp0s9"})
-        self.assertEqual({"enp0s9", "bond0", "bond0:0-16", "vlan200", "vlan200:0-17"}, dep3)
-
-        dep4 = anc.get_dependent_list(config, {"vlan200", "enp0s3"})
-        self.assertEqual({"vlan200", "enp0s3", "vlan200:0-17", "enp0s3:3-7"}, dep4)
-
-        dep5 = anc.get_dependent_list(config, {"enp0s4:5-17"})
-        self.assertEqual({"enp0s4:5-17"}, dep5)
-
-    def test_is_iface_missing_or_down(self):
-        dev_path = "/sys/devices/pci0000:00/net/enp0s8"
-        self._add_fs_mock({dev_path + "/operstate": "up",
-                           anc.DEVLINK_BASE_PATH + "enp0s8": (dev_path, )})
-
-        def check_result(value):
-            result = self._mocked_call([self._mock_fs], anc.is_iface_missing_or_down, "enp0s8")
-            self.assertEqual(value, result)
-
-        check_result(False)
-
-        self._fs.set_file_contents(anc.DEVLINK_BASE_PATH + "enp0s8/operstate", "down")
-        check_result(True)
-
-        self._fs.delete(anc.DEVLINK_BASE_PATH + "enp0s8")
-        check_result(True)
-
-    def test_get_updated_ifaces(self):
-        new_config = {"ifaces_types": {"enp0s3": anc.ETH,
-                                       "enp0s8": anc.ETH,
-                                       "enp0s9": anc.SLAVE,
-                                       "enp0s10": anc.SLAVE,
-                                       "bond0": anc.BONDING,
-                                       "bond1": anc.BONDING,
-                                       "vlan100": anc.VLAN,
-                                       "vlan200": anc.VLAN,
-                                       "enp0s3:1-1": anc.LABEL,
-                                       "enp0s8:2-4": anc.LABEL,
-                                       "bond0:5-14": anc.LABEL,
-                                       "bond1:6-16": anc.LABEL,
-                                       "vlan100:3-9": anc.LABEL,
-                                       "vlan200:4-11": anc.LABEL}}
-        up_list = ["enp0s3", "enp0s9", "enp0s10", "bond0", "vlan100",
-                   "enp0s8:2-4", "bond1:6-16", "vlan200:4-11"]
-        updated = anc.get_updated_ifaces(new_config, up_list)
-        self.assertEqual({"enp0s3", "enp0s8", "bond0", "bond1", "vlan100", "vlan200"}, updated)
-
-    def test_sort_ifaces_by_type(self):
-        config = {"ifaces_types": {"lo": anc.ETH,
-                                   "enp0s3": anc.ETH,
-                                   "enp0s8": anc.ETH,
-                                   "enp0s9": anc.SLAVE,
-                                   "enp0s10": anc.SLAVE,
-                                   "bond0": anc.BONDING,
-                                   "bond1": anc.BONDING,
-                                   "vlan100": anc.VLAN,
-                                   "vlan200": anc.VLAN,
-                                   "enp0s3:1-1": anc.LABEL,
-                                   "bond0:5-14": anc.LABEL,
-                                   "vlan100:3-9": anc.LABEL}}
-        ifaces = {"vlan100:3-9", "vlan200", "bond1", "bond0:5-14", "enp0s9",
-                  "enp0s8", "enp0s3", "enp0s3:1-1", "vlan100", "bond0", "enp0s10", "lo"}
-        sorted_ifaces = anc.sort_ifaces_by_type(config, ifaces, anc.UP_ORDER)
-        self.assertEqual(["enp0s3", "enp0s8", "lo", "bond0", "bond1", "vlan100",
-                          "vlan200", "bond0:5-14", "enp0s3:1-1", "vlan100:3-9"], sorted_ifaces)
-
-    def _test_set_iface_down(self, delete_ifstate):
-        etc_files = {
-            "interfaces": {
-                "auto": ["enp0s8", "enp0s8:2-3", "enp0s8:2-4"],
-                "enp0s8": {"address": "169.254.202.2/24"},
-                "enp0s8:2-3": {"address": "192.168.204.2/24"},
-                "enp0s8:2-4": {"address": "fd01::2/64"}},
-            "routes": [
-                {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "enp0s8", "metric": 1},
-                {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "enp0s8", "metric": 1}],
-            "routes6": [
-                {"net": "fa01:2::/64", "via": "fd01::111", "dev": "enp0s8", "metric": 1}],
-        }
-
-        self._add_fs_mock(FILE_GEN.generate_file_tree(etc_files=etc_files))
-        self._add_nw_mock(["enp0s8"])
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.apply_auto()
-
-        if delete_ifstate:
-            self._fs.delete(anc.IFSTATE_BASE_PATH + "enp0s8")
-
-        self.assertEqual(['enp0s8 UP 169.254.202.2/24 192.168.204.2/24 fd01::2/64'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['14.15.1.0/24 via 169.254.202.111 dev enp0s8 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev enp0s8 metric 1',
-                          'fa01:2::/64 via fd01::111 dev enp0s8 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._mocked_call([self._mock_fs, self._mock_syscmd, self._mock_logger],
-                          anc.set_iface_down, "enp0s8")
-
-        self.assertEqual(['enp0s8 DOWN'], self._nwmock.get_links_status())
-        self.assertEqual([], self._nwmock.get_routes())
-
-    def test_set_iface_down_ifstate_up(self):
-        self._test_set_iface_down(delete_ifstate=False)
-        self.assertEqual([('ifdown', 'enp0s8'),
-                          ('ip_link_set_down', 'enp0s8'),
-                          ('ip_addr_flush', 'enp0s8')],
-                          self._nwmock.get_history())
-
-    def test_set_iface_down_ifstate_down(self):
-        self._test_set_iface_down(delete_ifstate=True)
-        self.assertEqual([('ip_link_set_down', 'enp0s8'),
-                          ('ip_addr_flush', 'enp0s8')],
-                          self._nwmock.get_history())
-
-    def test_set_iface_down_error_messages(self):
-        def exec_sys_cmd(cmd):
-            if cmd.startswith("/sbin/ifdown"):
-                return 1, "< IFDOWN ERROR MESSAGE >\n"
-            if cmd.startswith("/usr/sbin/ip link set down"):
-                return 1, "< IP LINK SET DOWN ERROR MESSAGE >\n"
-            if cmd.startswith("/usr/sbin/ip addr flush"):
-                return 1, ("\n< IP ADDR FLUSH ERROR MESSAGE LINE 1 >\n"
-                           "< IP ADDR FLUSH ERROR MESSAGE LINE 2 >\n\n\n")
-            raise Exception(f"Unexpected system command: '{cmd}'")
-
-        dev_path = "/sys/devices/pci0000:00/net/enp0s8"
-        self._add_fs_mock({dev_path + "/operstate": "up",
-                           anc.DEVLINK_BASE_PATH + "enp0s8": (dev_path, ),
-                           anc.IFSTATE_BASE_PATH + "enp0s8": "enp0s8"})
-        self._add_logger_mock()
-
-        with mock.patch('src.bin.apply_network_config.execute_system_cmd', exec_sys_cmd):
-            self._mocked_call([self._mock_fs, self._mock_logger], anc.set_iface_down, "enp0s8")
-
-        self.assertEqual([
-            ('info', 'Bringing enp0s8 down'),
-            ('error', "Command 'ifdown' failed for interface enp0s8: '< IFDOWN ERROR MESSAGE >'"),
-            ('error', "Command 'ip link set down' failed for interface enp0s8: "
-                      "'< IP LINK SET DOWN ERROR MESSAGE >'"),
-            ('error', "Command 'ip addr flush' failed for interface enp0s8:\n"
-                      "< IP ADDR FLUSH ERROR MESSAGE LINE 1 >\n"
-                      "< IP ADDR FLUSH ERROR MESSAGE LINE 2 >")],
-            self._log.get_history())
-
-    def test_remove_iface_config_file(self):
-        self._add_logger_mock()
-
-        def run_function(path_exists: bool):
-            with(mock.patch('src.bin.apply_network_config.path_exists', return_value=path_exists),
-                 mock.patch('os.remove', side_effect=OSError("< OS ERROR >"))):
-                self._mocked_call([self._mock_logger], anc.remove_iface_config_file, "enp0s8")
-
-        run_function(False)
-        self.assertEqual([('info', 'File /etc/network/interfaces.d/ifcfg-enp0s8 does not exist, '
-                                   'no need to remove')], self._log.get_history())
-
-        self._log.reset_history()
-        run_function(True)
-        self.assertEqual([
-            ('info', 'Removing /etc/network/interfaces.d/ifcfg-enp0s8'),
-            ('error', 'Failed to remove /etc/network/interfaces.d/ifcfg-enp0s8: < OS ERROR >')],
-            self._log.get_history())
-
-    def _test_write_iface_config_file(self, has_existing_file):
-        path = anc.ETC_DIR + "/ifcfg-enp0s8"
-        contents = {path: "EXISTING CONTENTS\n"} if has_existing_file else None
-        self._add_fs_mock(contents)
-        with mock.patch('src.bin.apply_network_config.get_header', return_value=self._HEADER):
-            self._mocked_call([self._mock_fs],
-                              anc.write_iface_config_file, "enp0s8", self._IFACE_CONFIG)
-        contents = self._fs.get_file_contents(path)
-        self.assertEqual(self._IFACE_FILE, contents)
-
-    def test_write_iface_config_file_new(self):
-        self._test_write_iface_config_file(False)  # pylint: disable=no-value-for-parameter
-
-    def test_write_iface_config_file_existing(self):
-        self._test_write_iface_config_file(True)  # pylint: disable=no-value-for-parameter
-
-    _AUTO_LIST = ["lo", "enp0s3", "enp0s8", "vlan100"]
-
-    _AUTO_FILE = (f"{_HEADER}\n"
-                  "auto lo enp0s3 enp0s8 vlan100\n")
-
-    def _test_write_auto_file(self, has_existing_file):
-        path = anc.ETC_DIR + "/auto"
-        contents = {path: "EXISTING CONTENTS\n"} if has_existing_file else None
-        self._add_fs_mock(contents)
-        with mock.patch('src.bin.apply_network_config.get_header', return_value=self._HEADER):
-            self._mocked_call([self._mock_fs], anc.write_auto_file, self._AUTO_LIST)
-        contents = self._fs.get_file_contents(path)
-        self.assertEqual(self._AUTO_FILE, contents)
-
-    def test_write_auto_file_new(self):
-        self._test_write_auto_file(False)  # pylint: disable=no-value-for-parameter
-
-    def test_write_auto_file_existing(self):
-        self._test_write_auto_file(True)  # pylint: disable=no-value-for-parameter
-
-    def test_sort_properties(self):
-        props = ["other3", "allow-", "gateway", "other1", "mtu", "bond-miimon", "other2", "iface"]
-        sorted_props = anc.sort_properties(props)
-        self.assertEqual(["iface", "gateway", "bond-miimon", "mtu",
-                          "other1", "other2", "other3", "allow-"], sorted_props)
-
-    def test_get_route_entries(self):
-        self._add_fs_mock(
-            {anc.PUPPET_ROUTES_FILE:
-                "13.13.1.0 255.255.255.0 12.12.1.65 bond0 metric 1\n"
-                "13.13.2.0 255.255.255.0 12.12.3.37 enp0s8\n",
-             anc.PUPPET_ROUTES6_FILE:
-                "dead:beef:55:: ffff:ffff:ffff:ffff:: dead:beef::aa:1:453 bond0 metric 1\n"
-                "dead:beef:78:: ffff:ffff:ffff:ffff:: dead:beef:bb::bb:1:172 vlan200"})
-        self._add_logger_mock()
-
-        entries = self._mocked_call([self._mock_fs, self._mock_logger], anc.get_route_entries,
-                                    [anc.PUPPET_ROUTES_FILE, anc.PUPPET_ROUTES6_FILE])
-
-        self.assertEqual(['13.13.1.0 255.255.255.0 12.12.1.65 bond0 metric 1',
-                          '13.13.2.0 255.255.255.0 12.12.3.37 enp0s8',
-                          'dead:beef:55:: ffff:ffff:ffff:ffff:: dead:beef::aa:1:453 bond0 metric 1',
-                          'dead:beef:78:: ffff:ffff:ffff:ffff:: dead:beef:bb::bb:1:172 vlan200'],
-                          entries)
-        self.assertEqual([], self._log.get_history())
-
-    def test_get_route_entries_from_lines(self):
-        self._add_logger_mock()
-
-        contents = [
-            "# Comment 1",
-            "",
-            "        # Comment 2",
-            "\t   # Comment 3",
-            "13.13.1.0 255.255.255.0 12.12.1.65 bond0 metric 1",
-            "\t13.13.2.0\t255.255.255.0\t12.12.3.37\tenp0s8\t\t\t",
-            "    13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1    ",
-            "    13.13.4.0 255.255.255.0 12.12.4.16   ",
-            "  \t  dead:beef:55:: ffff:ffff:ffff:ffff:: dead:beef::aa:1:453 bond0 metric 1   ",
-            "    dead:beef:78:: ffff:ffff:ffff:ffff:: dead:beef:bb::bb:1:172 vlan200 metric 1\t"]
-
-        entries = self._mocked_call([self._mock_logger],
-                                    anc.get_route_entries_from_lines, contents, anc.ETC_ROUTES_FILE)
-
-        self.assertEqual([
-            '13.13.1.0 255.255.255.0 12.12.1.65 bond0 metric 1',
-            '13.13.2.0 255.255.255.0 12.12.3.37 enp0s8',
-            '13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1',
-            'dead:beef:55:: ffff:ffff:ffff:ffff:: dead:beef::aa:1:453 bond0 metric 1',
-            'dead:beef:78:: ffff:ffff:ffff:ffff:: dead:beef:bb::bb:1:172 vlan200 metric 1'],
-            entries)
-
-        self.assertEqual([(
-            'warning',
-            "Invalid route in file '/etc/network/routes', must have at least 4 "
-            "parameters, 3 found: '13.13.4.0 255.255.255.0 12.12.4.16'")],
-            self._log.get_history())
-
-    def test_get_route_iface(self):
-        self.assertEqual("vlan200", anc.get_route_iface("13.13.3.0 255.255.255.0 12.12.3.113 "
-                                                        "vlan200 metric 1"))
-
-    def test_create_route_obj_from_entry(self):
-        self.assertEqual({'ifname': 'enp0s8',
-                          'network': '13.13.2.0',
-                          'netmask': '255.255.255.0',
-                          'nexthop': '12.12.3.37'},
-                          anc.create_route_obj_from_entry(
-                              "13.13.2.0 255.255.255.0 12.12.3.37 enp0s8"))
-        self.assertEqual({'ifname': 'bond0',
-                          'network': '13.13.1.0',
-                          'netmask': '255.255.255.0',
-                          'nexthop': '12.12.1.65',
-                          'metric': '1'},
-                          anc.create_route_obj_from_entry(
-                              "13.13.1.0 255.255.255.0 12.12.1.65 bond0 metric 1"))
-
-    def test_get_prefix_length(self):
-        self.assertEqual(0, anc.get_prefix_length('0.0.0.0'))
-        self.assertEqual(1, anc.get_prefix_length('128.0.0.0'))
-        self.assertEqual(8, anc.get_prefix_length('255.0.0.0'))
-        self.assertEqual(31, anc.get_prefix_length('255.255.255.254'))
-
-        self.assertEqual(0, anc.get_prefix_length('0::'))
-        self.assertEqual(1, anc.get_prefix_length('8000::'))
-        self.assertEqual(16, anc.get_prefix_length('ffff::'))
-        self.assertEqual(127, anc.get_prefix_length('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe'))
-
-        def assert_fails(netmask):
-            exc = self.assertRaises(anc.InvalidNetmaskError, anc.get_prefix_length, netmask)
-            self.assertEqual(f"Failed to get prefix length, invalid netmask: '{netmask}'", str(exc))
-
-        assert_fails("2555.0.0.0")
-        assert_fails("255.0.255.0")
-        assert_fails("0.255.0.0")
-
-        assert_fails("fffff:ffff::")
-        assert_fails("ffff::ffff")
-        assert_fails("::ffff")
-
-    def test_get_linux_network(self):
-        self.assertEqual("192.168.1.0/24", anc.get_linux_network({"network": "192.168.1.0",
-                                                                  "netmask": "255.255.255.0"}))
-        self.assertEqual("default", anc.get_linux_network({"network": "default"}))
-
-    def _test_remove_route_entry_from_kernel(self, entry, return_code=0, stdout=""):
-        received_cmd = None
-
-        def exec_sys_cmd(cmd):
-            nonlocal received_cmd
-            received_cmd = cmd
-            return return_code, stdout
-
-        with mock.patch('src.bin.apply_network_config.execute_system_cmd', exec_sys_cmd):
-            self._mocked_call([self._mock_logger], anc.remove_route_entry_from_kernel, entry)
-
-        return received_cmd
-
-    def test_remove_route_entry_from_kernel_invalid_netmask(self):
-        self._add_logger_mock()
-        self._test_remove_route_entry_from_kernel("13.13.3.0 2555.255.255.0 12.12.3.113 "
-                                                  "vlan200 metric 1")
-        self.assertEqual([(
-            'error',
-            "Failed to remove route entry '13.13.3.0 2555.255.255.0 12.12.3.113 vlan200 "
-            "metric 1' from the kernel: Failed to get prefix length, invalid netmask: "
-            "'2555.255.255.0'")],
-            self._log.get_history())
-
-    def test_remove_route_entry_from_kernel_fail(self):
-        self._add_logger_mock()
-        self._test_remove_route_entry_from_kernel("13.13.3.0 255.255.255.0 12.12.3.113 "
-                                                  "vlan200 metric 1", 1, "< ERROR >")
-        self.assertEqual(
-            [('info', 'Removing route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1'),
-             ('error', "Failed removing route 13.13.3.0/24 via 12.12.3.113 dev vlan200 "
-                       "metric 1: '< ERROR >'")],
-            self._log.get_history())
-
-    def test_remove_route_entry_from_kernel_succeed(self):
-        self._add_logger_mock()
-        cmd = self._test_remove_route_entry_from_kernel("13.13.3.0 255.255.255.0 12.12.3.113 "
-                                                        "vlan200 metric 1")
-        self.assertEqual(
-            [('info', 'Removing route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1')],
-            self._log.get_history())
-        self.assertEqual(
-            "/usr/sbin/ip route del 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", cmd)
-
-    def test_get_route_description(self):
-        route_1 = {"network": "13.13.3.0", "netmask": "255.255.255.0",
-                   "nexthop": "12.12.3.113", "ifname": "vlan200"}
-        self.assertEqual("13.13.3.0/24 via 12.12.3.113 dev vlan200",
-                         anc.get_route_description(route_1))
-        self.assertEqual("13.13.3.0/24", anc.get_route_description(route_1, False))
-        route_1["metric"] = 1
-        self.assertEqual("13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1",
-                         anc.get_route_description(route_1))
-        self.assertEqual("13.13.3.0/24 metric 1", anc.get_route_description(route_1, False))
-
-        route_2 = {"network": "default", "nexthop": "12.12.3.113", "ifname": "vlan200"}
-        self.assertEqual("default via 12.12.3.113 dev vlan200", anc.get_route_description(route_2))
-        self.assertEqual("default", anc.get_route_description(route_2, False))
-        route_2["metric"] = 1
-        self.assertEqual("default via 12.12.3.113 dev vlan200 metric 1",
-                         anc.get_route_description(route_2))
-        self.assertEqual("default metric 1", anc.get_route_description(route_2, False))
-
-        route_3 = {"network": "aabb::", "netmask": "ffff:ffff:ffff:ffff::",
-                   "nexthop": "fe88::1", "ifname": "enp0s9"}
-        self.assertEqual("aabb::/64 via fe88::1 dev enp0s9", anc.get_route_description(route_3))
-        self.assertEqual("aabb::/64", anc.get_route_description(route_3, False))
-        route_3["metric"] = 1
-        self.assertEqual("aabb::/64 via fe88::1 dev enp0s9 metric 1",
-                         anc.get_route_description(route_3))
-        self.assertEqual("aabb::/64 metric 1", anc.get_route_description(route_3, False))
-
-        route_4 = {"network": "default", "nexthop": "fe88::1", "ifname": "enp0s9"}
-        self.assertEqual("default via fe88::1 dev enp0s9", anc.get_route_description(route_4))
-        self.assertEqual("default", anc.get_route_description(route_4, False))
-        route_4["metric"] = 1
-        self.assertEqual("default via fe88::1 dev enp0s9 metric 1",
-                         anc.get_route_description(route_4))
-        self.assertEqual("default metric 1", anc.get_route_description(route_4, False))
-
-    def _test_add_route_entry_to_kernel(self, entry, cmd_responses):
-        position = 0
-        self._add_logger_mock()
-
-        def exec_sys_cmd(cmd):
-            nonlocal position
-            pos = position
-            position += 1
-            self.assertEqual(cmd_responses[pos][0], cmd)
-            return cmd_responses[pos][1], cmd_responses[pos][2]
-
-        with mock.patch('src.bin.apply_network_config.execute_system_cmd', exec_sys_cmd):
-            self._mocked_call([self._mock_logger], anc.add_route_entry_to_kernel, entry)
-
-    def test_add_route_entry_to_kernel_existing(self):
-        self._test_add_route_entry_to_kernel(
-            "13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1",
-            (("/usr/sbin/ip route show 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0,
-              "13.13.3.0/24"), ))
-        self.assertEqual(
-            [('info', 'Adding route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1'),
-             ('info', 'Route already exists, skipping')],
-            self._log.get_history())
-
-    def test_add_route_entry_to_kernel_show_fail(self):
-        self._test_add_route_entry_to_kernel(
-            "13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1",
-            (("/usr/sbin/ip route show 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 1,
-              "< ERROR 1 >"),
-             ("/usr/sbin/ip route show 13.13.3.0/24 metric 1", 1, "< ERROR 2 >"),
-             ("/usr/sbin/ip route add 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0, "")))
-        self.assertEqual(
-            [('info', 'Adding route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1')],
-            self._log.get_history())
-
-    def test_add_route_entry_to_kernel_add_fail(self):
-        self._test_add_route_entry_to_kernel(
-            "13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1",
-            (("/usr/sbin/ip route show 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0, ""),
-             ("/usr/sbin/ip route show 13.13.3.0/24 metric 1", 0, ""),
-             ("/usr/sbin/ip route add 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 1,
-              "< ERROR >")))
-        self.assertEqual(
-            [('info', 'Adding route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1'),
-             ('error', "Failed adding route 13.13.3.0/24 via 12.12.3.113 dev "
-                       "vlan200 metric 1: '< ERROR >'")],
-            self._log.get_history())
-
-    def test_add_route_entry_to_kernel_replace_fail(self):
-        self._test_add_route_entry_to_kernel(
-            "13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1",
-            (("/usr/sbin/ip route show 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0, ""),
-             ("/usr/sbin/ip route show 13.13.3.0/24 metric 1", 0,
-              "13.13.3.0/24 via 12.12.3.1 dev vlan200"),
-             ("/usr/sbin/ip route replace 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 1,
-              "< ERROR >")))
-        self.assertEqual(
-            [('info', 'Adding route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1'),
-             ('info', 'Route to specified network already exists, replacing: 13.13.3.0/24 via '
-                      '12.12.3.1 dev vlan200'),
-             ('error', "Failed replacing route 13.13.3.0/24 via 12.12.3.113 dev "
-                       "vlan200 metric 1: '< ERROR >'")],
-            self._log.get_history())
-
-    def test_add_route_entry_to_kernel_add_succeed(self):
-        self._test_add_route_entry_to_kernel(
-            "13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1",
-            (("/usr/sbin/ip route show 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0, ""),
-             ("/usr/sbin/ip route show 13.13.3.0/24 metric 1", 0, ""),
-             ("/usr/sbin/ip route add 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0, "")))
-        self.assertEqual(
-            [('info', 'Adding route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1')],
-            self._log.get_history())
-
-    def test_add_route_entry_to_kernel_replace_succeed(self):
-        self._test_add_route_entry_to_kernel(
-            "13.13.3.0 255.255.255.0 12.12.3.113 vlan200 metric 1",
-            (("/usr/sbin/ip route show 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0, ""),
-             ("/usr/sbin/ip route show 13.13.3.0/24 metric 1", 0,
-              "13.13.3.0/24 via 12.12.3.1 dev vlan200"),
-             ("/usr/sbin/ip route replace 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1", 0,
-              "")))
-        self.assertEqual(
-            [('info', 'Adding route: 13.13.3.0/24 via 12.12.3.113 dev vlan200 metric 1'),
-             ('info', 'Route to specified network already exists, replacing: 13.13.3.0/24 via '
-                      '12.12.3.1 dev vlan200')],
-            self._log.get_history())
-
-    def _test_update_routes(self, etc_routes, puppet_routes, updated_ifaces=None):
-        links = ["enc10", "enc11", "enc12", "enc13"]
-        self._add_fs_mock(FILE_GEN.generate_file_tree(
-            puppet_files={
-                "routes": [route for route in puppet_routes if ":" not in route["net"]],
-                "routes6": [route for route in puppet_routes if ":" in route["net"]]
-            },
-            etc_files={
-                "interfaces": {
-                    "auto": links,
-                    "enc10": {"address": "10.10.10.3/24"},
-                    "enc11": {"address": "10.10.11.3/24"},
-                    "enc12": {"address": "fd12::3/64"},
-                    "enc13": {"address": "fd13::3/64"},
-                },
-                "routes": etc_routes,
-            }
-        ))
-        self._add_nw_mock(links)
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.apply_auto()
-
-        if updated_ifaces:
-            for iface in updated_ifaces:
-                self._nwmock.ifdown(iface)
-                self._nwmock.ifup(iface)
-
-        with mock.patch('src.bin.apply_network_config.get_header', return_value=self._HEADER):
-            self._mocked_call([self._mock_fs, self._mock_syscmd, self._mock_sysinv_lock,
-                               self._mock_logger], anc.update_routes, updated_ifaces)
-
-    def test_update_routes(self):
-        self._test_update_routes(
-            etc_routes=[
-                {"net": "10.33.1.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-                {"net": "10.33.2.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-                {"net": "10.33.3.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-                {"net": "fd33:1::/64", "via": "fd12::101", "dev": "enc12", "metric": 1},
-                {"net": "fd33:2::/64", "via": "fd12::101", "dev": "enc12", "metric": 1},
-                {"net": "fd33:3::/64", "via": "fd12::101", "dev": "enc12", "metric": 1}],
-            puppet_routes=[
-                {"net": "10.33.1.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-                {"net": "10.33.2.0/24", "via": "10.10.10.202", "dev": "enc10", "metric": 1},
-                {"net": "10.33.4.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-                {"net": "fd33:1::/64", "via": "fd12::101", "dev": "enc12", "metric": 1},
-                {"net": "fd33:2::/64", "via": "fd12::202", "dev": "enc12", "metric": 1},
-                {"net": "fd33:4::/64", "via": "fd12::101", "dev": "enc12", "metric": 1}])
-
-        self.assertEqual([
-            '10.33.1.0/24 via 10.10.10.101 dev enc10 metric 1',
-            'fd33:1::/64 via fd12::101 dev enc12 metric 1',
-            '10.33.2.0/24 via 10.10.10.202 dev enc10 metric 1',
-            '10.33.4.0/24 via 10.10.10.101 dev enc10 metric 1',
-            'fd33:2::/64 via fd12::202 dev enc12 metric 1',
-            'fd33:4::/64 via fd12::101 dev enc12 metric 1'],
-            self._nwmock.get_routes())
-
-        self.assertEqual([
-            ('info', 'Differences found between /var/run/network-scripts.puppet/routes and '
-                     '/etc/network/routes'),
-            ('info', 'Removing route: 10.33.2.0/24 via 10.10.10.101 dev enc10 metric 1'),
-            ('info', 'Removing route: 10.33.3.0/24 via 10.10.10.101 dev enc10 metric 1'),
-            ('info', 'Removing route: fd33:2::/64 via fd12::101 dev enc12 metric 1'),
-            ('info', 'Removing route: fd33:3::/64 via fd12::101 dev enc12 metric 1'),
-            ('info', 'Route not previously present in /etc/network/routes, adding'),
-            ('info', 'Adding route: 10.33.2.0/24 via 10.10.10.202 dev enc10 metric 1'),
-            ('info', 'Route not previously present in /etc/network/routes, adding'),
-            ('info', 'Adding route: 10.33.4.0/24 via 10.10.10.101 dev enc10 metric 1'),
-            ('info', 'Route not previously present in /etc/network/routes, adding'),
-            ('info', 'Adding route: fd33:2::/64 via fd12::202 dev enc12 metric 1'),
-            ('info', 'Route not previously present in /etc/network/routes, adding'),
-            ('info', 'Adding route: fd33:4::/64 via fd12::101 dev enc12 metric 1')],
-            self._log.get_history())
-
-        self.assertEqual(
-            self._HEADER + "\n"
-            "10.33.1.0 255.255.255.0 10.10.10.101 enc10 metric 1\n"
-            "10.33.2.0 255.255.255.0 10.10.10.202 enc10 metric 1\n"
-            "10.33.4.0 255.255.255.0 10.10.10.101 enc10 metric 1\n"
-            "fd33:1:: ffff:ffff:ffff:ffff:: fd12::101 enc12 metric 1\n"
-            "fd33:2:: ffff:ffff:ffff:ffff:: fd12::202 enc12 metric 1\n"
-            "fd33:4:: ffff:ffff:ffff:ffff:: fd12::101 enc12 metric 1\n",
-            self._fs.get_file_contents(anc.ETC_ROUTES_FILE))
-
-    def test_update_routes_updated_interfaces(self):
-        routes = [
-            {"net": "10.33.1.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-            {"net": "10.33.2.0/24", "via": "10.10.10.101", "dev": "enc10", "metric": 1},
-            {"net": "10.33.3.0/24", "via": "10.10.11.101", "dev": "enc11", "metric": 1},
-            {"net": "10.33.4.0/24", "via": "10.10.11.101", "dev": "enc11", "metric": 1},
-            {"net": "fd33:1::/64", "via": "fd12::101", "dev": "enc12", "metric": 1},
-            {"net": "fd33:2::/64", "via": "fd12::101", "dev": "enc12", "metric": 1},
-            {"net": "fd33:3::/64", "via": "fd13::101", "dev": "enc13", "metric": 1},
-            {"net": "fd33:4::/64", "via": "fd13::101", "dev": "enc13", "metric": 1}]
-        self._test_update_routes(routes, routes, ["enc11", "enc13"])
-
-        self.assertEqual([
-            '10.33.1.0/24 via 10.10.10.101 dev enc10 metric 1',
-            '10.33.2.0/24 via 10.10.10.101 dev enc10 metric 1',
-            'fd33:1::/64 via fd12::101 dev enc12 metric 1',
-            'fd33:2::/64 via fd12::101 dev enc12 metric 1',
-            '10.33.3.0/24 via 10.10.11.101 dev enc11 metric 1',
-            '10.33.4.0/24 via 10.10.11.101 dev enc11 metric 1',
-            'fd33:3::/64 via fd13::101 dev enc13 metric 1',
-            'fd33:4::/64 via fd13::101 dev enc13 metric 1'],
-            self._nwmock.get_routes())
-
-        self.assertEqual([
-            ('info', 'No differences found between /var/run/network-scripts.puppet/routes and '
-                     '/etc/network/routes'),
-            ('info', 'Route is associated with and updated interface, adding'),
-            ('info', 'Adding route: 10.33.3.0/24 via 10.10.11.101 dev enc11 metric 1'),
-            ('info', 'Route is associated with and updated interface, adding'),
-            ('info', 'Adding route: 10.33.4.0/24 via 10.10.11.101 dev enc11 metric 1'),
-            ('info', 'Route is associated with and updated interface, adding'),
-            ('info', 'Adding route: fd33:3::/64 via fd13::101 dev enc13 metric 1'),
-            ('info', 'Route is associated with and updated interface, adding'),
-            ('info', 'Adding route: fd33:4::/64 via fd13::101 dev enc13 metric 1')],
-            self._log.get_history())
-
-        self.assertEqual(
-            self._HEADER + "\n"
-            "10.33.1.0 255.255.255.0 10.10.10.101 enc10 metric 1\n"
-            "10.33.2.0 255.255.255.0 10.10.10.101 enc10 metric 1\n"
-            "10.33.3.0 255.255.255.0 10.10.11.101 enc11 metric 1\n"
-            "10.33.4.0 255.255.255.0 10.10.11.101 enc11 metric 1\n"
-            "fd33:1:: ffff:ffff:ffff:ffff:: fd12::101 enc12 metric 1\n"
-            "fd33:2:: ffff:ffff:ffff:ffff:: fd12::101 enc12 metric 1\n"
-            "fd33:3:: ffff:ffff:ffff:ffff:: fd13::101 enc13 metric 1\n"
-            "fd33:4:: ffff:ffff:ffff:ffff:: fd13::101 enc13 metric 1\n",
-            self._fs.get_file_contents(anc.ETC_ROUTES_FILE))
-
-    def test_check_cloud_init_valid(self):
-        static_links = ["lo", "ens1f0"]
-        self._add_fs_mock({
-            anc.ETC_DIR + "/auto": FILE_GEN.generate_auto_file(static_links),
-            anc.ETC_DIR + "/ifcfg-ens1f0":
-                FILE_GEN.generate_ifcfg_file("ens1f0", {"address": "fd05::2/64",
-                                                        "gateway": "fd05::111"}),
-            anc.SUBCLOUD_ENROLLMENT_FILE: '',
-            anc.CLOUD_INIT_FILE:
-                "# This file is generated from information provided by the datasource.  Changes\n"
-                "# to it will not persist across an instance reboot.  To disable cloud-init's\n"
-                "# network configuration capabilities, write a file\n"
-                "# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:\n"
-                "# network: {config: disabled}\n"
-                "auto lo\n"
-                "iface lo inet loopbackauto vlan401\n"
-                "iface vlan401 inet6 static\n"
-                "    address 2620:10a:a001:d41::163/64\n"
-                "    gateway 2620:10a:a001:d41::1\n"
-                "    vlan-raw-device ens1f0\n"
-                "    vlan_id 401\n"})
-
-        self._add_nw_mock(static_links)
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.set_allow_multiple_default_gateways(True)
-        self._nwmock.apply_auto()
-        self._nwmock.ifup("vlan401")
-        self._nwmock.ifdown("ens1f0")
-        self._nwmock.ifup("ens1f0")
-
-        self._mocked_call([self._mock_fs, self._mock_syscmd, self._mock_logger],
-                          anc.check_enrollment_config)
-
-        self.assertEqual(['default via 2620:10a:a001:d41::1 dev vlan401 metric 1024'],
-                          self._nwmock.get_routes())
-
-        self.assertEqual([
-            ('info', "Enrollment: Parsing file '/etc/network/interfaces.d/50-cloud-init'"),
-            ('info', 'Enrollment: Configuring interface vlan401 with gateway '
-                     '2620:10a:a001:d41::1'),
-            ('info', 'Adding route: default via 2620:10a:a001:d41::1 dev vlan401'),
-            ('info', 'Route to specified network already exists, replacing: default via fd05::111 '
-                     'dev ens1f0 metric 1024 pref medium')],
-            self._log.get_history())
-
-    def test_check_cloud_init_multiple_ifaces(self):
-        static_links = ["lo", "ens1f0"]
-        self._add_fs_mock({
-            anc.ETC_DIR + "/auto": FILE_GEN.generate_auto_file(static_links),
-            anc.ETC_DIR + "/ifcfg-ens1f0":
-                FILE_GEN.generate_ifcfg_file("ens1f0", {"address": "fd05::2/64",
-                                                        "gateway": "fd05::111"}),
-            anc.SUBCLOUD_ENROLLMENT_FILE: '',
-            anc.CLOUD_INIT_FILE:
-                "auto lo\n"
-                "iface lo inet loopbackauto vlan401\n"
-                "iface vlan401 inet6 static\n"
-                "    address 2620:10a:a001:d41::163/64\n"
-                "    gateway 2620:10a:a001:d41::1\n"
-                "    vlan-raw-device ens1f0\n"
-                "    vlan_id 401\n"
-                "iface vlan402 inet6 static\n"
-                "    address eb22:303::55:2/64\n"
-                "    gateway eb22:303::1\n"
-                "    vlan-raw-device ens1f0\n"
-                "    vlan_id 402\n"})
-
-        self._add_nw_mock(static_links)
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.set_allow_multiple_default_gateways(True)
-        self._nwmock.apply_auto()
-        self._nwmock.ifup("vlan401")
-        self._nwmock.ifup("vlan402")
-        self._nwmock.ifdown("ens1f0")
-        self._nwmock.ifup("ens1f0")
-
-        self._mocked_call([self._mock_fs, self._mock_syscmd, self._mock_logger],
-                          anc.check_enrollment_config)
-
-        self.assertEqual(['default via eb22:303::1 dev vlan402 metric 1024'],
-                          self._nwmock.get_routes())
-
-        self.assertEqual([
-            ('info', "Enrollment: Parsing file '/etc/network/interfaces.d/50-cloud-init'"),
-            ('warning', 'Enrollment: Multiple interfaces with gateway for ipv6 found: vlan401, '
-                        'vlan402'),
-            ('info', 'Enrollment: Configuring interface vlan401 with gateway '
-                     '2620:10a:a001:d41::1'),
-            ('info', 'Adding route: default via 2620:10a:a001:d41::1 dev vlan401'),
-            ('info', 'Route to specified network already exists, replacing: default via fd05::111 '
-                     'dev ens1f0 metric 1024 pref medium'),
-            ('info', 'Enrollment: Configuring interface vlan402 with gateway eb22:303::1'),
-            ('info', 'Adding route: default via eb22:303::1 dev vlan402'),
-            ('info', 'Route to specified network already exists, replacing: default via '
-                     '2620:10a:a001:d41::1 dev vlan401 metric 1024 pref medium')],
-            self._log.get_history())
-
-    def test_check_cloud_init_empty(self):
-        self._add_fs_mock({
-            anc.SUBCLOUD_ENROLLMENT_FILE: '',
-            anc.CLOUD_INIT_FILE: ''})
-
-        self._add_logger_mock()
-
-        self._mocked_call([self._mock_fs, self._mock_logger], anc.check_enrollment_config)
-
-        self.assertEqual([
-            ('info', "Enrollment: Parsing file '/etc/network/interfaces.d/50-cloud-init'"),
-            ('warning', 'Enrollment: Could not find any valid interface config in '
-                        "'/etc/network/interfaces.d/50-cloud-init'")],
-            self._log.get_history())
-
-    def test_check_cloud_init_invalid_gateway(self):
-        self._add_fs_mock({
-            anc.SUBCLOUD_ENROLLMENT_FILE: '',
-            anc.CLOUD_INIT_FILE:
-                "auto lo\n"
-                "iface lo inet loopbackauto vlan401\n"
-                "iface vlan401 inet6 static\n"
-                "    address 2620:10a:a001:d41::163/64\n"
-                "    gateway h620::1\n"
-                "    vlan-raw-device ens1f0\n"
-                "    vlan_id 401\n"})
-
-        self._add_logger_mock()
-
-        self._mocked_call([self._mock_fs, self._mock_logger], anc.check_enrollment_config)
-
-        self.assertEqual([
-            ('info', "Enrollment: Parsing file '/etc/network/interfaces.d/50-cloud-init'"),
-            ('warning', "Enrollment: Invalid gateway address 'h620::1' for interface 'vlan401'"),
-            ('warning', 'Enrollment: No interface with gateway address found, skipping')],
-            self._log.get_history())
-
-    def test_disable_kickstart_pxeboot(self):
-        etc_cfg = {
-            "interfaces": {
-                "auto": ["lo", "enp0s8"],
-                "lo": {},
-                "enp0s8": {}, },
-        }
-
-        puppet_cfg = {
-            "interfaces": {
-                "auto": ["lo", "enp0s8", "enp0s8:2-3", "enp0s8:2-4"],
-                "lo": {},
-                "enp0s8": {"address": "169.254.202.2/24"},
-                "enp0s8:2-3": {"address": "192.168.204.2/24"},
-                "enp0s8:2-4": {"address": "fd01::2/64"}},
-        }
-
-        contents = FILE_GEN.generate_file_tree(puppet_files=puppet_cfg, etc_files=etc_cfg)
-        contents[anc.ETC_DIR + "/ifcfg-pxeboot"] = (
-            "auto enp0s8:2\n"
-            "iface enp0s8:2 inet dhcp\n"
-            "    post-up  echo 0 > /proc/sys/net/ipv6/conf/enp0s8/autoconf; "
-                         "echo 0 > /proc/sys/net/ipv6/conf/enp0s8/accept_ra; "  # noqa: E131
-                         "echo 0 > /proc/sys/net/ipv6/conf/enp0s8/accept_redirects\n")
-
-        self._add_fs_mock(contents)
-        self._add_nw_mock(["lo", "enp0s8"])
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.apply_auto()
-
-        self._mocked_call([self._mock_fs, self._mock_syscmd,
-                           self._mock_sysinv_lock, self._mock_logger], anc.update_interfaces)
-
-        self.assertEqual([
-            ('info', 'Turn off pxeboot install config for enp0s8:2, will be turned on later'),
-            ('info', 'Bringing enp0s8:2 down'),
-            ('info', 'Remove ifcfg-pxeboot, left from kickstart install phase'),
-            ('info', 'Removing /etc/network/interfaces.d/ifcfg-pxeboot')],
-            self._log.get_history()[:4])
-
-    def test_execute_system_cmd(self):
-        retcode, stdout = anc.execute_system_cmd('echo "test_execute_system_cmd"')
-        self.assertEqual(0, retcode)
-        self.assertEqual("test_execute_system_cmd\n", stdout)
-
-    _OS_GETPGID = os.getpgid
-
-    def test_execute_system_cmd_timeout_retcode_15(self):
-        subproc_pid = None
-        subproc_pgid = None
-
-        def getpgid(pid):
-            nonlocal subproc_pid
-            nonlocal subproc_pgid
-            subproc_pid = pid
-            subproc_pgid = self._OS_GETPGID(pid)
-            return subproc_pgid
-
-        self._add_logger_mock()
-
-        with mock.patch("os.getpgid", getpgid):
-            retcode, stdout = self._mocked_call([self._mock_logger], anc.execute_system_cmd,
-                                                "tests/system_cmd_test_script.sh 15", 1)
-
-        self.assertEqual(15, retcode)
-        self.assertEqual("< BEFORE SLEEP >\nTerminated\n< SIGTERM RECEIVED >\n", stdout)
-        self.assertEqual([
-            (LoggerMock.WARNING,
-             "Execution time exceeded for command 'tests/system_cmd_test_script.sh 15', sending "
-             f"SIGTERM to subprocess (pid={subproc_pid}, pgid={subproc_pgid})")],
-             self._log.get_history())
-
-    def test_execute_system_cmd_timeout_retcode_0(self):
-        subproc_pid = None
-        subproc_pgid = None
-
-        def getpgid(pid):
-            nonlocal subproc_pid
-            nonlocal subproc_pgid
-            subproc_pid = pid
-            subproc_pgid = self._OS_GETPGID(pid)
-            return subproc_pgid
-
-        self._add_logger_mock()
-
-        with mock.patch("os.getpgid", getpgid):
-            retcode, stdout = self._mocked_call([self._mock_logger], anc.execute_system_cmd,
-                                                "tests/system_cmd_test_script.sh 0", 1)
-
-        self.assertEqual(0, retcode)
-        self.assertEqual("< BEFORE SLEEP >\nTerminated\n< SIGTERM RECEIVED >\n", stdout)
-        self.assertEqual([
-            (LoggerMock.WARNING,
-             "Execution time exceeded for command 'tests/system_cmd_test_script.sh 0', sending "
-             f"SIGTERM to subprocess (pid={subproc_pid}, pgid={subproc_pgid})"),
-            (LoggerMock.INFO,
-             "Command 'tests/system_cmd_test_script.sh 0' output:\n"
-             '< BEFORE SLEEP >\n'
-             'Terminated\n'
-             '< SIGTERM RECEIVED >')],
-             self._log.get_history())
-
-    def test_execute_system_cmd_timeout_kill(self):
-        subproc_pid = None
-        subproc_pgid = None
-
-        def getpgid(pid):
-            nonlocal subproc_pid
-            nonlocal subproc_pgid
-            subproc_pid = pid
-            subproc_pgid = self._OS_GETPGID(pid)
-            return subproc_pgid
-
-        self._add_logger_mock()
-
-        with (mock.patch("os.getpgid", getpgid),
-              mock.patch("src.bin.apply_network_config.TERM_WAIT_TIME", 1)):
-            retcode, stdout = self._mocked_call([self._mock_logger], anc.execute_system_cmd,
-                                                "tests/system_cmd_test_script.sh 0 -e", 1)
-
-        self.assertEqual(-9, retcode)
-        self.assertEqual("< BEFORE SLEEP >\nTerminated\n< SIGTERM RECEIVED >\n", stdout)
-        self.assertEqual([
-            (LoggerMock.WARNING,
-             "Execution time exceeded for command 'tests/system_cmd_test_script.sh 0 -e', sending "
-             f"SIGTERM to subprocess (pid={subproc_pid}, pgid={subproc_pgid})"),
-            (LoggerMock.WARNING,
-             "Command 'tests/system_cmd_test_script.sh 0 -e' has not terminated after "
-             f"1 seconds, sending SIGKILL to subprocess "
-             f"(pid={subproc_pid}, pgid={subproc_pgid})")],
-             self._log.get_history())
-
-
-class TestInterfaceDependencies(BaseTestCase):
-
-    _AUTO = ["enp0s3", "enp0s3:1-9", "enp0s8", "enp0s8:2-13", "enp0s8:3-15",
-             "datavlan300", "datavlan300:6-22", "enp0s9", "enp0s10", "bond0",
-             "bond0:4-12", "vlan200", "vlan200:5-19"]
-
-    _BASE_CFG = {
-        "interfaces": {
-            "auto": _AUTO,
-            "enp0s3": {},
-            "enp0s3:1-9": {"address": "12.12.15.67/24", "gateway": "12.12.15.1"},
-            "enp0s8": {},
-            "enp0s8:2-13": {"address": "192.168.204.2/24"},
-            "enp0s8:3-15": {"address": "192.168.206.2/24"},
-            "datavlan300": {"raw_dev": "enp0s8", "vlan_id": 300},
-            "datavlan300:6-22": {"address": "adad:efef::44:55:66/64",
-                                 "raw_dev": "enp0s8", "vlan_id": 300},
-            "enp0s9": {"master": "bond0"},
-            "enp0s10": {"master": "bond0"},
-            "bond0": {"slaves": ["enp0s9", "enp0s10"], "hwaddress": "08:00:27:f2:66:72"},
-            "bond0:4-12": {"address": "11.22.3.15/24", "slaves": ["enp0s9", "enp0s10"],
-                           "hwaddress": "08:00:27:f2:66:72"},
-            "vlan200": {"raw_dev": "bond0"},
-            "vlan200:5-19": {"address": "dead:beef::1:2:3/64", "raw_dev": "bond0"}}
-    }
-
-    _MODIFIED_CFG = {
-        "auto": _AUTO,
-        "enp0s3": {"mtu": 9000},
-        "enp0s3:1-9": {"mtu": 9000, "address": "12.12.15.67/24", "gateway": "12.12.15.1"},
-        "enp0s8": {"mtu": 9000},
-        "enp0s8:2-13": {"mtu": 9000, "address": "192.168.204.2/24"},
-        "enp0s8:3-15": {"mtu": 9000, "address": "192.168.206.2/24"},
-        "datavlan300": {"mtu": 9000, "raw_dev": "enp0s8", "vlan_id": 300},
-        "datavlan300:6-22": {"mtu": 9000, "address": "adad:efef::44:55:66/64",
-                             "raw_dev": "enp0s8", "vlan_id": 300},
-        "enp0s9": {"mtu": 9000, "master": "bond0"},
-        "enp0s10": {"mtu": 9000, "master": "bond0"},
-        "bond0": {"mtu": 9000, "slaves": ["enp0s9", "enp0s10"], "hwaddress": "08:00:27:f2:66:72"},
-        "bond0:4-12": {"mtu": 9000, "address": "11.22.3.15/24", "slaves": ["enp0s9", "enp0s10"],
-                       "hwaddress": "08:00:27:f2:66:72"},
-        "vlan200": {"mtu": 9000, "raw_dev": "bond0"},
-        "vlan200:5-19": {"mtu": 9000, "address": "dead:beef::1:2:3/64", "raw_dev": "bond0"}
-    }
-
-    _STATIC_LINKS = ["lo", "enp0s3", "enp0s8", "enp0s9", "enp0s10"]
-
-    _FS = ReadOnlyFileContainer(FILE_GEN.generate_file_tree(_BASE_CFG, _BASE_CFG))
-
-    _MODIFIED_FILES = {k: FILE_GEN.generate_ifcfg_file(k, v)
-                       for k, v in _MODIFIED_CFG.items() if k != "auto"}
-
-    def _setup_scenario(self, modified_ifaces):
-        contents = dict()
-        for iface in modified_ifaces:
-            path = anc.ETC_DIR + "/ifcfg-" + iface
-            contents[path] = self._MODIFIED_FILES[iface]
-        self._fs = FilesystemMock(fs=self._FS, contents=contents)
-        self._add_nw_mock(self._STATIC_LINKS)
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.apply_auto()
-
-    def _run_update_interfaces(self):
-        self._mocked_call([self._mock_fs, self._mock_syscmd,
-                           self._mock_sysinv_lock, self._mock_logger], anc.update_interfaces)
-
-    def test_modify_label(self):
-        self._setup_scenario(["enp0s3:1-9"])
-        self._run_update_interfaces()
-        self.assertEqual([("ifdown", "enp0s3:1-9"),
-                          ("ifup", "enp0s3:1-9")],
-                          self._nwmock.get_history())
-
-    def test_modify_eth_with_label(self):
-        self._setup_scenario(["enp0s3"])
-        self._run_update_interfaces()
-        self.assertEqual([('ifdown', 'enp0s3:1-9'),
-                          ('ifdown', 'enp0s3'),
-                          ('ip_link_set_down', 'enp0s3'),
-                          ('ip_addr_flush', 'enp0s3'),
-                          ('ifup', 'enp0s3'),
-                          ('ifup', 'enp0s3:1-9')],
-                          self._nwmock.get_history())
-
-    def test_modify_vlan_over_eth(self):
-        self._setup_scenario(["datavlan300"])
-        self._run_update_interfaces()
-        self.assertEqual([("ifdown", "datavlan300:6-22"),
-                          ("ifdown", "datavlan300"),
-                          ("ifup", "datavlan300"),
-                          ("ifup", "datavlan300:6-22")],
-                          self._nwmock.get_history())
-
-    def test_modify_vlan_over_bonding(self):
-        self._setup_scenario(["vlan200"])
-        self._run_update_interfaces()
-        self.assertEqual([("ifdown", "vlan200:5-19"),
-                          ("ifdown", "vlan200"),
-                          ("ifup", "vlan200"),
-                          ("ifup", "vlan200:5-19")],
-                          self._nwmock.get_history())
-
-    def test_modify_eth_with_vlan(self):
-        self._setup_scenario(["enp0s8"])
-        self._run_update_interfaces()
-        self.assertEqual([('ifdown', 'datavlan300:6-22'),
-                          ('ifdown', 'enp0s8:2-13'),
-                          ('ifdown', 'enp0s8:3-15'),
-                          ('ifdown', 'datavlan300'),
-                          ('ifdown', 'enp0s8'),
-                          ('ip_link_set_down', 'enp0s8'),
-                          ('ip_addr_flush', 'enp0s8'),
-                          ('ifup', 'enp0s8'),
-                          ('ifup', 'datavlan300'),
-                          ('ifup', 'datavlan300:6-22'),
-                          ('ifup', 'enp0s8:2-13'),
-                          ('ifup', 'enp0s8:3-15')],
-                          self._nwmock.get_history())
-
-    def test_modify_bonding(self):
-        self._setup_scenario(["bond0"])
-        self._run_update_interfaces()
-        self.assertEqual([('ifdown', 'bond0:4-12'),
-                          ('ifdown', 'vlan200:5-19'),
-                          ('ifdown', 'vlan200'),
-                          ('ifdown', 'bond0'),
-                          ('ifup', 'bond0'),
-                          ('ifup', 'vlan200'),
-                          ('ifup', 'bond0:4-12'),
-                          ('ifup', 'vlan200:5-19')],
-                          self._nwmock.get_history())
-
-    def test_modify_slave(self):
-        self._setup_scenario(["enp0s9"])
-        self._run_update_interfaces()
-        self.assertEqual([('ifdown', 'bond0:4-12'),
-                          ('ifdown', 'vlan200:5-19'),
-                          ('ifdown', 'vlan200'),
-                          ('ifdown', 'bond0'),
-                          ('ifup', 'bond0'),
-                          ('ifup', 'vlan200'),
-                          ('ifup', 'bond0:4-12'),
-                          ('ifup', 'vlan200:5-19')],
-                          self._nwmock.get_history())
-
-
-class MigrationBaseTestCase(BaseTestCase):
-    def _setup_scenario(self, from_cfg, to_cfg, static_links):
-        self._add_fs_mock(FILE_GEN.generate_file_tree(to_cfg, from_cfg))
-        self._add_nw_mock(static_links)
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._nwmock.apply_auto()
-
-    def _run_apply_config(self):
-        self._mocked_call([self._mock_fs, self._mock_syscmd,
-                           self._mock_sysinv_lock, self._mock_logger], anc.apply_config, False)
-
-    def _check_etc_file_list(self, to_cfg):
-        files = self._fs.get_file_list(anc.ETC_DIR)
-        etc_ifaces = []
-        has_auto = False
-        for file in files:
-            if file.startswith("ifcfg-"):
-                etc_ifaces.append(file.split("-", 1)[1])
-            elif file == "auto":
-                has_auto = True
-            else:
-                raise Exception(f"Unexpected file in ETC dir: '{file}'")
-        self.assertEqual(True, has_auto, "'auto' file not present in ETC dir")
-        self.assertEqual(sorted(to_cfg["interfaces"]["auto"]), etc_ifaces)
-
-
-class TestEthAndLoMigration(MigrationBaseTestCase):
-
-    _LEFT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "lo", "lo:2-3", "lo:2-4",
-                     "lo:3-5", "lo:3-6", "enp0s9", "enp0s9:7-11", "enp0s9:7-12"],
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "lo": {},
-            "lo:2-3": {"address": "192.168.204.2/24"},
-            "lo:2-4": {"address": "fd01::2/64"},
-            "lo:3-5": {"address": "192.168.206.2/24"},
-            "lo:3-6": {"address": "fd02::2/64"},
-            "enp0s9": {},
-            "enp0s9:7-11": {"address": "112.44.202.26/24"},
-            "enp0s9:7-12": {"address": "ad60:b00::202:26/64"},
-            },
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "enp0s3", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "lo", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "lo", "metric": 1},
-            {"net": "14.14.4.0/24", "via": "112.44.202.111", "dev": "enp0s9", "metric": 1}],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "enp0s3", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "lo", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "lo", "metric": 1},
-            {"net": "fa01:4::/64", "via": "ad60:b00::111", "dev": "enp0s9", "metric": 1}],
-    }
-
-    _RIGHT = {
-        "interfaces": {
-            "auto": ["enp0s9", "enp0s9:1-1", "enp0s9:1-2", "lo", "enp0s8", "enp0s8:2-3",
-                     "enp0s8:2-4", "enp0s8:3-5", "enp0s8:3-6", "enp0s3", "enp0s3:7-11",
-                     "enp0s3:7-12"],
-            "enp0s9": {},
-            "enp0s9:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s9:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "lo": {},
-            "enp0s8": {"address": "169.254.202.2/24"},
-            "enp0s8:2-3": {"address": "192.168.204.2/24"},
-            "enp0s8:2-4": {"address": "fd01::2/64"},
-            "enp0s8:3-5": {"address": "192.168.206.2/24"},
-            "enp0s8:3-6": {"address": "fd02::2/64"},
-            "enp0s3": {},
-            "enp0s3:7-11": {"address": "112.44.202.26/24"},
-            "enp0s3:7-12": {"address": "ad60:b00::202:26/64"},
-            },
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "enp0s9", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.4.0/24", "via": "112.44.202.111", "dev": "enp0s3", "metric": 1}],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "enp0s9", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "enp0s8", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "enp0s8", "metric": 1},
-            {"net": "fa01:4::/64", "via": "ad60:b00::111", "dev": "enp0s3", "metric": 1}],
-    }
-
-    _STATIC_LINKS = ["lo", "enp0s3", "enp0s8", "enp0s9"]
-
-    def test_eth_to_eth_migration_a(self):
-        self._setup_scenario(self._LEFT, self._RIGHT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual([
-            'enp0s3 UP 112.44.202.26/24 ad60:b00::202:26/64',
-            'enp0s8 UP 169.254.202.2/24 192.168.204.2/24 192.168.206.2/24 fd01::2/64 fd02::2/64',
-            'enp0s9 UP 10.20.1.2/24 fd00::1:2/64',
-            'lo UP'],
-            self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s9',
-                          'default via fd00::1 dev enp0s9 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev enp0s9 metric 1',
-                          '14.15.1.0/24 via 169.254.202.111 dev enp0s8 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev enp0s8 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev enp0s8 metric 1',
-                          '14.14.4.0/24 via 112.44.202.111 dev enp0s3 metric 1',
-                          'fa01:1::/64 via fd00::111 dev enp0s9 metric 1',
-                          'fa01:2::/64 via fd01::111 dev enp0s8 metric 1',
-                          'fa01:3::/64 via fd02::111 dev enp0s8 metric 1',
-                          'fa01:4::/64 via ad60:b00::111 dev enp0s3 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._RIGHT)
-
-    def test_eth_to_eth_migration_b(self):
-        self._setup_scenario(self._RIGHT, self._LEFT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual([
-            'enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-            'enp0s8 DOWN',
-            'enp0s9 UP 112.44.202.26/24 ad60:b00::202:26/64',
-            'lo UP 192.168.204.2/24 192.168.206.2/24 fd01::2/64 fd02::2/64'],
-            self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev enp0s3 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev lo metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev lo metric 1',
-                          '14.14.4.0/24 via 112.44.202.111 dev enp0s9 metric 1',
-                          'fa01:1::/64 via fd00::111 dev enp0s3 metric 1',
-                          'fa01:2::/64 via fd01::111 dev lo metric 1',
-                          'fa01:3::/64 via fd02::111 dev lo metric 1',
-                          'fa01:4::/64 via ad60:b00::111 dev enp0s9 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._LEFT)
-
-
-class TestEthToVLANMigration(MigrationBaseTestCase):
-
-    _LEFT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "enp0s8",
-                     "enp0s8:2-3", "enp0s8:2-4", "enp0s8:3-5", "enp0s8:3-6"],
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "enp0s8": {"address": "169.254.202.2/24"},
-            "enp0s8:2-3": {"address": "192.168.204.2/24"},
-            "enp0s8:2-4": {"address": "fd01::2/64"},
-            "enp0s8:3-5": {"address": "192.168.206.2/24"},
-            "enp0s8:3-6": {"address": "fd02::2/64"}},
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "enp0s3", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "enp0s8", "metric": 1}],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "enp0s3", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "enp0s8", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "enp0s8", "metric": 1}],
-    }
-
-    _RIGHT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "enp0s8", "vlan100", "vlan200",
-                     "vlan100:2-3", "vlan100:2-4", "vlan200:3-5", "vlan200:3-6"],
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "enp0s8": {"address": "169.254.202.2/24"},
-            "vlan100": {"raw_dev": "enp0s8"},
-            "vlan100:2-3": {"address": "192.168.204.2/24", "raw_dev": "enp0s8"},
-            "vlan100:2-4": {"address": "fd01::2/64", "raw_dev": "enp0s8"},
-            "vlan200": {"raw_dev": "enp0s8"},
-            "vlan200:3-5": {"address": "192.168.206.2/24", "raw_dev": "enp0s8"},
-            "vlan200:3-6": {"address": "fd02::2/64", "raw_dev": "enp0s8"}},
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "oam0", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "pxeboot0", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "vlan100", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "vlan200", "metric": 1}],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "oam0", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "vlan100", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "vlan200", "metric": 1}],
-    }
-
-    _STATIC_LINKS = ["enp0s3", "enp0s8"]
-
-    def test_eth_to_vlan_migration(self):
-        self._setup_scenario(self._LEFT, self._RIGHT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual(['enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-                          'enp0s8 UP 169.254.202.2/24',
-                          'vlan100 UP VLAN(enp0s8,100) 192.168.204.2/24 fd01::2/64',
-                          'vlan200 UP VLAN(enp0s8,200) 192.168.206.2/24 fd02::2/64'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024',
-                          '14.14.2.0/24 via 192.168.204.111 dev vlan100 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev vlan200 metric 1',
-                          'fa01:2::/64 via fd01::111 dev vlan100 metric 1',
-                          'fa01:3::/64 via fd02::111 dev vlan200 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._RIGHT)
-
-    def test_vlan_to_eth_migration(self):
-        self._setup_scenario(self._RIGHT, self._LEFT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual(['enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-                          'enp0s8 UP 169.254.202.2/24 192.168.204.2/24 192.168.206.2/24 fd01::2/64 '
-                          'fd02::2/64'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev enp0s3 metric 1',
-                          '14.15.1.0/24 via 169.254.202.111 dev enp0s8 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev enp0s8 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev enp0s8 metric 1',
-                          'fa01:1::/64 via fd00::111 dev enp0s3 metric 1',
-                          'fa01:2::/64 via fd01::111 dev enp0s8 metric 1',
-                          'fa01:3::/64 via fd02::111 dev enp0s8 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._LEFT)
-
-
-class TestEthToBondingMigration(MigrationBaseTestCase):
-
-    _LEFT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "enp0s8",
-                     "enp0s8:2-3", "enp0s8:2-4", "enp0s8:3-5", "enp0s8:3-6"],
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "enp0s8": {"address": "169.254.202.2/24"},
-            "enp0s8:2-3": {"address": "192.168.204.2/24"},
-            "enp0s8:2-4": {"address": "fd01::2/64"},
-            "enp0s8:3-5": {"address": "192.168.206.2/24"},
-            "enp0s8:3-6": {"address": "fd02::2/64"}},
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "enp0s3", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "enp0s8", "metric": 1}],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "enp0s3", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "enp0s8", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "enp0s8", "metric": 1}],
-    }
-
-    _RIGHT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s8", "oam0", "oam0:1-1", "oam0:1-2",
-                     "enp0s9", "enp0s10", "pxeboot0", "vlan100", "vlan100:2-3",
-                     "vlan100:2-4", "vlan200", "vlan200:3-5", "vlan200:3-6"],
-            "enp0s3": {"master": "oam0"},
-            "enp0s8": {"master": "oam0"},
-            "oam0": {"slaves": ["enp0s3", "enp0s8"], "hwaddress": "08:00:27:f2:66:72"},
-            "oam0:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1",
-                         "slaves": ["enp0s3", "enp0s8"], "hwaddress": "08:00:27:f2:66:72"},
-            "oam0:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1",
-                         "slaves": ["enp0s3", "enp0s8"], "hwaddress": "08:00:27:f2:66:72"},
-            "enp0s9": {"master": "pxeboot0"},
-            "enp0s10": {"master": "pxeboot0"},
-            "pxeboot0": {"address": "169.254.202.2/24", "slaves": ["enp0s9", "enp0s10"],
-                         "hwaddress": "08:00:27:f2:67:11"},
-            "vlan100": {"raw_dev": "pxeboot0"},
-            "vlan100:2-3": {"address": "192.168.204.2/24", "raw_dev": "pxeboot0"},
-            "vlan100:2-4": {"address": "fd01::2/64", "raw_dev": "pxeboot0"},
-            "vlan200": {"raw_dev": "pxeboot0"},
-            "vlan200:3-5": {"address": "192.168.206.2/24", "raw_dev": "pxeboot0"},
-            "vlan200:3-6": {"address": "fd02::2/64", "raw_dev": "pxeboot0"}},
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "oam0", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "pxeboot0", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "vlan100", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "vlan200", "metric": 1}],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "oam0", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "vlan100", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "vlan200", "metric": 1}],
-    }
-
-    _STATIC_LINKS = ["enp0s3", "enp0s8", "enp0s9", "enp0s10"]
-
-    def test_eth_to_bonding_migration(self):
-        self._setup_scenario(self._LEFT, self._RIGHT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual(['enp0s10 UP SLAVE(pxeboot0)',
-                          'enp0s3 UP SLAVE(oam0)',
-                          'enp0s8 UP SLAVE(oam0)',
-                          'enp0s9 UP SLAVE(pxeboot0)',
-                          'oam0 UP BONDING(enp0s3,enp0s8) 10.20.1.2/24 fd00::1:2/64',
-                          'pxeboot0 UP BONDING(enp0s9,enp0s10) 169.254.202.2/24',
-                          'vlan100 UP VLAN(pxeboot0,100) 192.168.204.2/24 fd01::2/64',
-                          'vlan200 UP VLAN(pxeboot0,200) 192.168.206.2/24 fd02::2/64'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev oam0',
-                          'default via fd00::1 dev oam0 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev oam0 metric 1',
-                          '14.15.1.0/24 via 169.254.202.111 dev pxeboot0 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev vlan100 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev vlan200 metric 1',
-                          'fa01:1::/64 via fd00::111 dev oam0 metric 1',
-                          'fa01:2::/64 via fd01::111 dev vlan100 metric 1',
-                          'fa01:3::/64 via fd02::111 dev vlan200 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._RIGHT)
-
-    def test_bonding_to_eth_migration(self):
-        self._setup_scenario(self._RIGHT, self._LEFT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual(['enp0s10 DOWN',
-                          'enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-                          'enp0s8 UP 169.254.202.2/24 192.168.204.2/24 192.168.206.2/24 '
-                                    'fd01::2/64 fd02::2/64',
-                          'enp0s9 DOWN'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev enp0s3 metric 1',
-                          '14.15.1.0/24 via 169.254.202.111 dev enp0s8 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev enp0s8 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev enp0s8 metric 1',
-                          'fa01:1::/64 via fd00::111 dev enp0s3 metric 1',
-                          'fa01:2::/64 via fd01::111 dev enp0s8 metric 1',
-                          'fa01:3::/64 via fd02::111 dev enp0s8 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._LEFT)
-
-
-class TestBondingMigration(MigrationBaseTestCase):
-    _LEFT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "enp0s8", "enp0s8:2-3", "enp0s8:2-4",
-                     "enp0s8:3-5", "enp0s8:3-6", "enp0s9", "enp0s10", "data0", "data0:4-7",
-                     "data0:4-8", "data1", "data1:5-9", "data1:5-10"],
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "enp0s8": {"address": "169.254.202.2/24"},
-            "enp0s8:2-3": {"address": "192.168.204.2/24"},
-            "enp0s8:2-4": {"address": "fd01::2/64"},
-            "enp0s8:3-5": {"address": "192.168.206.2/24"},
-            "enp0s8:3-6": {"address": "fd02::2/64"},
-            "enp0s9": {"master": "data0"},
-            "enp0s10": {"master": "data0"},
-            "data0": {"slaves": ["enp0s9", "enp0s10"], "hwaddress": "08:00:27:f2:66:72"},
-            "data0:4-7": {"address": "112.154.1.2/24", "slaves": ["enp0s9", "enp0s10"],
-                          "hwaddress": "08:00:27:f2:66:72"},
-            "data0:4-8": {"address": "fc01:154:1::2/64", "slaves": ["enp0s9", "enp0s10"],
-                          "hwaddress": "08:00:27:f2:66:72"},
-            "data1": {"raw_dev": "data0", "vlan_id": 50},
-            "data1:5-9": {"address": "112.155.1.2/24", "raw_dev": "data0", "vlan_id": 50},
-            "data1:5-10": {"address": "fc01:155:1::2/64", "raw_dev": "data0", "vlan_id": 50}},
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "enp0s3", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "enp0s8", "metric": 1},
-            {"net": "14.14.4.0/24", "via": "112.154.1.111", "dev": "data0", "metric": 1},
-            {"net": "14.14.5.0/24", "via": "112.155.1.111", "dev": "data1", "metric": 1},
-        ],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "enp0s3", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "enp0s8", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "enp0s8", "metric": 1},
-            {"net": "fa01:4::/64", "via": "fc01:154:1::111", "dev": "data0", "metric": 1},
-            {"net": "fa01:5::/64", "via": "fc01:155:1::111", "dev": "data1", "metric": 1},
-        ],
-    }
-
-    _RIGHT = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "enp0s8", "enp0s10:2-3", "enp0s10:2-4",
-                     "enp0s10:3-5", "enp0s10:3-6", "enp0s9", "enp0s10", "data0", "data0:4-7",
-                     "data0:4-8", "data1", "data1:5-9", "data1:5-10"],
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "enp0s8": {"master": "data0"},
-            "enp0s9": {"master": "data0"},
-            "enp0s10": {"address": "169.254.202.2/24"},
-            "enp0s10:2-3": {"address": "192.168.204.2/24"},
-            "enp0s10:2-4": {"address": "fd01::2/64"},
-            "enp0s10:3-5": {"address": "192.168.206.2/24"},
-            "enp0s10:3-6": {"address": "fd02::2/64"},
-            "data0": {"slaves": ["enp0s8", "enp0s9"], "hwaddress": "08:00:27:f2:66:72"},
-            "data0:4-7": {"address": "112.154.1.2/24", "slaves": ["enp0s8", "enp0s9"],
-                          "hwaddress": "08:00:27:f2:66:72"},
-            "data0:4-8": {"address": "fc01:154:1::2/64", "slaves": ["enp0s8", "enp0s9"],
-                          "hwaddress": "08:00:27:f2:66:72"},
-            "data1": {"raw_dev": "data0", "vlan_id": 50},
-            "data1:5-9": {"address": "112.155.1.2/24", "raw_dev": "data0", "vlan_id": 50},
-            "data1:5-10": {"address": "fc01:155:1::2/64", "raw_dev": "data0", "vlan_id": 50}},
-        "routes": [
-            {"net": "14.14.1.0/24", "via": "10.20.1.111", "dev": "enp0s3", "metric": 1},
-            {"net": "14.15.1.0/24", "via": "169.254.202.111", "dev": "enp0s10", "metric": 1},
-            {"net": "14.14.2.0/24", "via": "192.168.204.111", "dev": "enp0s10", "metric": 1},
-            {"net": "14.14.3.0/24", "via": "192.168.206.111", "dev": "enp0s10", "metric": 1},
-            {"net": "14.14.4.0/24", "via": "112.154.1.111", "dev": "data0", "metric": 1},
-            {"net": "14.14.5.0/24", "via": "112.155.1.111", "dev": "data1", "metric": 1},
-        ],
-        "routes6": [
-            {"net": "fa01:1::/64", "via": "fd00::111", "dev": "enp0s3", "metric": 1},
-            {"net": "fa01:2::/64", "via": "fd01::111", "dev": "enp0s10", "metric": 1},
-            {"net": "fa01:3::/64", "via": "fd02::111", "dev": "enp0s10", "metric": 1},
-            {"net": "fa01:4::/64", "via": "fc01:154:1::111", "dev": "data0", "metric": 1},
-            {"net": "fa01:5::/64", "via": "fc01:155:1::111", "dev": "data1", "metric": 1},
-        ],
-    }
-
-    _STATIC_LINKS = ["enp0s3", "enp0s8", "enp0s9", "enp0s10"]
-
-    def test_bonding_migration_a(self):
-        self._setup_scenario(self._LEFT, self._RIGHT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual(['data0 UP BONDING(enp0s8,enp0s9) 112.154.1.2/24 fc01:154:1::2/64',
-                          'data1 UP VLAN(data0,50) 112.155.1.2/24 fc01:155:1::2/64',
-                          'enp0s10 UP 169.254.202.2/24 192.168.204.2/24 192.168.206.2/24 '
-                                     'fd01::2/64 fd02::2/64',
-                          'enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-                          'enp0s8 UP SLAVE(data0)',
-                          'enp0s9 UP SLAVE(data0)'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev enp0s3 metric 1',
-                          'fa01:1::/64 via fd00::111 dev enp0s3 metric 1',
-                          '14.15.1.0/24 via 169.254.202.111 dev enp0s10 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev enp0s10 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev enp0s10 metric 1',
-                          '14.14.4.0/24 via 112.154.1.111 dev data0 metric 1',
-                          '14.14.5.0/24 via 112.155.1.111 dev data1 metric 1',
-                          'fa01:2::/64 via fd01::111 dev enp0s10 metric 1',
-                          'fa01:3::/64 via fd02::111 dev enp0s10 metric 1',
-                          'fa01:4::/64 via fc01:154:1::111 dev data0 metric 1',
-                          'fa01:5::/64 via fc01:155:1::111 dev data1 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._RIGHT)
-
-    def test_bonding_migration_b(self):
-        self._setup_scenario(self._RIGHT, self._LEFT, self._STATIC_LINKS)
-
-        self._run_apply_config()
-
-        self.assertEqual(['data0 UP BONDING(enp0s9,enp0s10) 112.154.1.2/24 fc01:154:1::2/64',
-                          'data1 UP VLAN(data0,50) 112.155.1.2/24 fc01:155:1::2/64',
-                          'enp0s10 UP SLAVE(data0)',
-                          'enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-                          'enp0s8 UP 169.254.202.2/24 192.168.204.2/24 192.168.206.2/24 '
-                                    'fd01::2/64 fd02::2/64',
-                          'enp0s9 UP SLAVE(data0)'],
-                          self._nwmock.get_links_status())
-
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024',
-                          '14.14.1.0/24 via 10.20.1.111 dev enp0s3 metric 1',
-                          'fa01:1::/64 via fd00::111 dev enp0s3 metric 1',
-                          '14.15.1.0/24 via 169.254.202.111 dev enp0s8 metric 1',
-                          '14.14.2.0/24 via 192.168.204.111 dev enp0s8 metric 1',
-                          '14.14.3.0/24 via 192.168.206.111 dev enp0s8 metric 1',
-                          '14.14.4.0/24 via 112.154.1.111 dev data0 metric 1',
-                          '14.14.5.0/24 via 112.155.1.111 dev data1 metric 1',
-                          'fa01:2::/64 via fd01::111 dev enp0s8 metric 1',
-                          'fa01:3::/64 via fd02::111 dev enp0s8 metric 1',
-                          'fa01:4::/64 via fc01:154:1::111 dev data0 metric 1',
-                          'fa01:5::/64 via fc01:155:1::111 dev data1 metric 1'],
-                          self._nwmock.get_routes())
-
-        self._check_etc_file_list(self._LEFT)
-
-
-class TestUpgrade(BaseTestCase):
-    _CFG = {
-        "interfaces": {
-            "auto": ["enp0s3", "enp0s3:1-1", "enp0s3:1-2", "enp0s8", "enp0s8:2-3", "enp0s8:2-4"],
-            "lo": {},
-            "enp0s3": {},
-            "enp0s3:1-1": {"address": "10.20.1.2/24", "gateway": "10.20.1.1"},
-            "enp0s3:1-2": {"address": "fd00::1:2/64", "gateway": "fd00::1"},
-            "enp0s8": {"address": "169.254.202.2/24"},
-            "enp0s8:2-3": {"address": "192.168.204.2/24"},
-            "enp0s8:2-4": {"address": "fd01::2/64"}},
-    }
-
-    _MIN_CFG = {
-        "interfaces": {
-            "lo": {},
-        }
-    }
-
-    _STATIC_LINKS = ["enp0s3", "enp0s8"]
-
-    def _setup_scenario(self, fs_contents):
-        self._add_fs_mock(fs_contents)
-        self._add_nw_mock(self._STATIC_LINKS)
-        self._add_scmd_mock()
-        self._add_logger_mock()
-        self._fs.set_file_contents(anc.UPGRADE_FILE, '')
-        self._nwmock.apply_auto()
-
-    def _run_update_interfaces(self):
-        self._mocked_call([self._mock_fs, self._mock_syscmd,
-                           self._mock_sysinv_lock, self._mock_logger], anc.update_interfaces)
-
-    def test_upgrade_no_change(self):
-        self._setup_scenario(FILE_GEN.generate_file_tree(
-            etc_files=self._CFG, puppet_files=self._CFG))
-        self._run_update_interfaces()
-        self.assertEqual([
-            ('info', 'Upgrade bootstrap is in execution'),
-            ('info', 'Configuring interface enp0s3'),
-            ('info', 'Configuring interface enp0s8'),
-            ('info', 'Configuring interface enp0s3:1-1'),
-            ('info', "Link already has address '10.20.1.2/24', no need to set label up"),
-            ('info', 'Adding route: default via 10.20.1.1 dev enp0s3'),
-            ('info', 'Route already exists, skipping'),
-            ('info', 'Configuring interface enp0s3:1-2'),
-            ('info', "Link already has address 'fd00::1:2/64', no need to set label up"),
-            ('info', 'Adding route: default via fd00::1 dev enp0s3'),
-            ('info', 'Route already exists, skipping'),
-            ('info', 'Configuring interface enp0s8:2-3'),
-            ('info', "Link already has address '192.168.204.2/24', no need to set label up"),
-            ('info', 'Configuring interface enp0s8:2-4'),
-            ('info', "Link already has address 'fd01::2/64', no need to set label up")],
-            self._log.get_history())
-
-    def test_upgrade_none_configured(self):
-        self._setup_scenario(FILE_GEN.generate_file_tree(
-            etc_files=self._MIN_CFG, puppet_files=self._CFG))
-        self._run_update_interfaces()
-        self.assertEqual(['enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-                          'enp0s8 UP 169.254.202.2/24 192.168.204.2/24 fd01::2/64'],
-                         self._nwmock.get_links_status())
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024'],
-                         self._nwmock.get_routes())
-        self.assertEqual([
-            ('info', 'Upgrade bootstrap is in execution'),
-            ('info', 'Configuring interface enp0s3'),
-            ('info', "Interface 'enp0s3' is missing or down, flushing IPs and bringing up"),
-            ('info', 'Bringing enp0s3 up'),
-            ('info', 'Configuring interface enp0s8'),
-            ('info', "Interface 'enp0s8' is missing or down, flushing IPs and bringing up"),
-            ('info', 'Bringing enp0s8 up'),
-            ('info', 'Configuring interface enp0s3:1-1'),
-            ('info', 'Bringing enp0s3:1-1 up'),
-            ('info', 'Configuring interface enp0s3:1-2'),
-            ('info', 'Bringing enp0s3:1-2 up'),
-            ('info', 'Configuring interface enp0s8:2-3'),
-            ('info', 'Bringing enp0s8:2-3 up'),
-            ('info', 'Configuring interface enp0s8:2-4'),
-            ('info', 'Bringing enp0s8:2-4 up')],
-            self._log.get_history())
-
-    def test_upgrade_already_configured(self):
-        self._setup_scenario(FILE_GEN.generate_file_tree(
-            etc_files=self._MIN_CFG, puppet_files=self._CFG))
-        self._nwmock.ip_link_set_up("enp0s3")
-        self._nwmock.ip_addr_add("10.20.1.2/24", "enp0s3")
-        self._nwmock.ip_addr_add("fd00::1:2/64", "enp0s3")
-        self._nwmock.ip_route_add("default", "10.20.1.111", "enp0s3", "1")
-        self._nwmock.ip_link_set_up("enp0s8")
-        self._nwmock.ip_addr_add("192.168.208.2/24", "enp0s8")
-        self._run_update_interfaces()
-        self.assertEqual([
-            'enp0s3 UP 10.20.1.2/24 fd00::1:2/64',
-            'enp0s8 UP 169.254.202.2/24 192.168.204.2/24 192.168.208.2/24 fd01::2/64'],
-            self._nwmock.get_links_status())
-        self.assertEqual(['default via 10.20.1.1 dev enp0s3',
-                          'default via fd00::1 dev enp0s3 metric 1024'],
-                         self._nwmock.get_routes())
-        self.assertEqual([
-            ('info', 'Upgrade bootstrap is in execution'),
-            ('info', 'Configuring interface enp0s3'),
-            ('info', 'Configuring interface enp0s8'),
-            ('info', 'Adding IP 169.254.202.2/24 to interface enp0s8'),
-            ('info', 'Configuring interface enp0s3:1-1'),
-            ('info', "Link already has address '10.20.1.2/24', no need to set label up"),
-            ('info', 'Adding route: default via 10.20.1.1 dev enp0s3'),
-            ('info', 'Route to specified network already exists, replacing: default via '
-                     '10.20.1.111 dev enp0s3 metric 1'),
-            ('info', 'Configuring interface enp0s3:1-2'),
-            ('info', "Link already has address 'fd00::1:2/64', no need to set label up"),
-            ('info', 'Adding route: default via fd00::1 dev enp0s3'),
-            ('info', 'Configuring interface enp0s8:2-3'),
-            ('info', 'Bringing enp0s8:2-3 up'),
-            ('info', 'Configuring interface enp0s8:2-4'),
-            ('info', 'Bringing enp0s8:2-4 up')],
-            self._log.get_history())
diff --git a/puppet-manifests/tox.ini b/puppet-manifests/tox.ini
index 8f2550882..5345c8efd 100644
--- a/puppet-manifests/tox.ini
+++ b/puppet-manifests/tox.ini
@@ -10,26 +10,12 @@
 # and then run "tox" from this directory.
 [tox]
 toxworkdir = /tmp/{env:USER}_puppet-manifests
-envlist = py39,puppetlint
+envlist = puppetlint
 skipsdist = True
 
 [testenv]
 recreate = True
 
-[testenv:py39]
-basepython = python3.9
-sitepackages = False
-
-setenv = VIRTUAL_ENV={envdir}
-         OS_TEST_PATH=./tests
-
-deps =
-    -r{toxinidir}/test-requirements.txt
-
-commands =
-  stestr run {posargs}
-  stestr slowest
-
 [testenv:puppetlint]
 # Note: centos developer env requires ruby-devel
 # Ubuntu developer env requires ruby-dev
diff --git a/pylint.rc b/pylint.rc
index 08da8b746..215dcbf3a 100755
--- a/pylint.rc
+++ b/pylint.rc
@@ -123,10 +123,8 @@ enable=E1603,E1609,E1610,E1602,E1606,E1608,E1607,E1605,E1604,E1601,E1611,W1652,
 # See "Messages Control" section of
 # https://pylint.readthedocs.io/en/latest/user_guide
 # We are disabling (C)onvention
-#  W0201: attribute-defined-outside-init
-#  W1202: logging-format-interpolation
 #  W1618: no-absolute-import
-disable=C, W0201,W1202,W1618
+disable=C, W1618
 
 [REPORTS]
 # Set the output format. Available formats are text, parseable, colorized, msvs
diff --git a/test-requirements.txt b/test-requirements.txt
index 612ccf7b3..b1fba817e 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,5 +4,3 @@ bashate >= 0.2
 bandit!=1.6.0,>=1.1.0,<2.0.0;python_version>="3.0" # GPLv2
 shellcheck-py;python_version>="3.0" # MIT
 netaddr >= 0.7.19
-mock>=2.0.0
-testtools>=1.4.0
diff --git a/tox.ini b/tox.ini
index 80595b923..37539c1d7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -141,7 +141,7 @@ description =
 
 
 commands =
-    flake8 puppet-manifests
+    flake8 puppet-manifests/src/modules/platform/files
 
 [testenv:pylint]
 basepython = python3
@@ -155,17 +155,11 @@ commands =
 
 [flake8]
 # E123, E125 skipped as they are invalid PEP-8.
-# E126 continuation line over-indented for hanging indent
-# E127 continuation line over-indented for visual indent
-# H104: File contains nothing but comments
-# H306: imports not in alphabetical order
-# H404: multi line docstring should start without a leading new line
+# E501 skipped because some of the code files include templates
+#      that end up quite wide
 # H405: multi line docstring summary not separated with an empty line
-# W504: line break after binary operator
 show-source = True
-ignore = E123,E125,E126,E127,H104,H306,H404,H405,W504
-# Max line length set to 100 to coincide with opendev's code view width
-max-line-length = 100
+ignore = E123,E125,E501,H405,W504
 exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,release-tag-*
 
 [testenv:bandit]