From e32033f9950c264c220fc4af11a761eabc789bef Mon Sep 17 00:00:00 2001 From: Till Maas Date: Mon, 16 Jul 2018 09:16:24 +0200 Subject: [PATCH] Use black as formatter and enable flake8/pylint --- README.md | 1 + library/network_connections.py | 2883 +++++++++++++++++----------- tests/test_network_connections.py | 2953 +++++++++++++---------------- tox.ini | 25 +- 4 files changed, 3133 insertions(+), 2729 deletions(-) diff --git a/README.md b/README.md index 5b2b22a..dc4599b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ linux-system-roles/network ========================== [![Coverage Status](https://coveralls.io/repos/github/linux-system-roles/network/badge.svg)](https://coveralls.io/github/linux-system-roles/network) [![Travis Build Status](https://travis-ci.org/linux-system-roles/network.svg?branch=master)](https://travis-ci.org/linux-system-roles/network) +[![Code Style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) This role enables users to configure network on target machines. The role can be used to configure: diff --git a/library/network_connections.py b/library/network_connections.py index 02e330d..d57494b 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -2,61 +2,68 @@ # -*- coding: utf-8 -*- # SPDX-License-Identifier: BSD-3-Clause -DOCUMENTATION=''' ---- -module: network_connections -author: "Thomas Haller (thaller@redhat.com)" -short_description: module for network role to manage connection profiles -requirements: for 'nm' provider requires pygobject, dbus and NetworkManager. -version_added: "2.0" -description: Manage networking profiles (connections) for NetworkManager and initscripts - networking providers. -options: Documentation needs to be written. Note that the network_connections module - tightly integrates with the network role and currently it is not expected to use - this module outside the role. Thus, consult README.md for examples for the role. -''' - import functools import os import socket import sys import traceback +DOCUMENTATION = """ +--- +module: network_connections +author: "Thomas Haller (thaller@redhat.com)" +short_description: module for network role to manage connection profiles +requirements: for 'nm' provider requires pygobject, dbus and NetworkManager. +version_added: "2.0" +description: Manage networking profiles (connections) for NetworkManager and + initscripts networking providers. +options: Documentation needs to be written. Note that the network_connections + module tightly integrates with the network role and currently it is not + expected to use this module outside the role. Thus, consult README.md for + examples for the role. +""" + + ############################################################################### + class CheckMode: - PREPARE = 'prepare' - DRY_RUN = 'dry-run' - PRE_RUN = 'pre-run' - REAL_RUN = 'real-run' - DONE = 'done' + PREPARE = "prepare" + DRY_RUN = "dry-run" + PRE_RUN = "pre-run" + REAL_RUN = "real-run" + DONE = "done" + class LogLevel: - ERROR = 'error' - WARN = 'warn' - INFO = 'info' - DEBUG = 'debug' + ERROR = "error" + WARN = "warn" + INFO = "info" + DEBUG = "debug" @staticmethod def fmt(level): - return '<%-6s' % (str(level) + '>') + return "<%-6s" % (str(level) + ">") + class MyError(Exception): pass + class ValidationError(MyError): def __init__(self, name, message): - Exception.__init__(self, name + ': ' + message) + Exception.__init__(self, name + ": " + message) self.error_message = message self.name = name @staticmethod def from_connection(idx, message): - return ValidationError('connection[' + str(idx) + ']', message) + return ValidationError("connection[" + str(idx) + "]", message) # cmp() is not available in python 3 anymore if "cmp" not in dir(__builtins__): + def cmp(x, y): """ Replacement for built-in function cmp that was removed in Python 3 @@ -71,12 +78,12 @@ if "cmp" not in dir(__builtins__): class Util: - PY3 = (sys.version_info[0] == 3) + PY3 = sys.version_info[0] == 3 - STRING_TYPE = (str if PY3 else basestring) + STRING_TYPE = str if PY3 else basestring # noqa:F821 @staticmethod - def first(iterable, default = None, pred = None): + def first(iterable, default=None, pred=None): for v in iterable: if pred is None or pred(v): return v @@ -85,16 +92,16 @@ class Util: @staticmethod def check_output(argv): # subprocess.check_output is python 2.7. - with open('/dev/null', 'wb') as DEVNULL: + with open("/dev/null", "wb") as DEVNULL: import subprocess + env = os.environ.copy() - env['LANG'] = 'C' - p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=DEVNULL, - env=env) + env["LANG"] = "C" + p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=DEVNULL, env=env) # FIXME: Can we assume this to always be UTF-8? out = p.communicate()[0].decode("UTF-8") if p.returncode != 0: - raise MyError('failure calling %s: exit with %s' % (argv, p.returncode)) + raise MyError("failure calling %s: exit with %s" % (argv, p.returncode)) return out @classmethod @@ -104,17 +111,23 @@ class Util: @classmethod def NM(cls): - n = getattr(cls, '_NM', None) + n = getattr(cls, "_NM", None) if n is None: + # Installing pygobject in a tox virtualenv does not work out of the + # box + # pylint: disable=import-error import gi - gi.require_version('NM', '1.0') + + gi.require_version("NM", "1.0") from gi.repository import NM, GLib, Gio, GObject + cls._NM = NM cls._GLib = GLib cls._Gio = Gio cls._GObject = GObject n = NM import uuid + cls._uuid = uuid return n @@ -139,14 +152,14 @@ class Util: @classmethod def GMainLoop(cls): - gmainloop = getattr(cls, '_GMainLoop', None) + gmainloop = getattr(cls, "_GMainLoop", None) if gmainloop is None: gmainloop = cls.GLib().MainLoop() cls._GMainLoop = gmainloop return gmainloop @classmethod - def GMainLoop_run(cls, timeout = None): + def GMainLoop_run(cls, timeout=None): if timeout is None: cls.GMainLoop().run() return True @@ -154,10 +167,12 @@ class Util: GLib = cls.GLib() result = [] loop = cls.GMainLoop() + def _timeout_cb(unused): result.append(1) loop.quit() return False + timeout_id = GLib.timeout_add(int(timeout * 1000), _timeout_cb, None) loop.run() if result: @@ -166,7 +181,7 @@ class Util: return True @classmethod - def GMainLoop_iterate(cls, may_block = False): + def GMainLoop_iterate(cls, may_block=False): return cls.GMainLoop().get_context().iteration(may_block) @classmethod @@ -184,7 +199,10 @@ class Util: def error_is_cancelled(cls, e): GLib = cls.GLib() if isinstance(e, GLib.GError): - if e.domain == 'g-io-error-quark' and e.code == cls.Gio().IOErrorEnum.CANCELLED: + if ( + e.domain == "g-io-error-quark" + and e.code == cls.Gio().IOErrorEnum.CANCELLED + ): return True return False @@ -193,11 +211,11 @@ class Util: # see dev_valid_name() in kernel's net/core/dev.c if not ifname: return False - if ifname in [ '.', '..' ]: + if ifname in [".", ".."]: return False if len(ifname) >= 16: return False - if any([c == '/' or c == ':' or c.isspace() for c in ifname]): + if any([c == "/" or c == ":" or c.isspace() for c in ifname]): return False # FIXME: encoding issues regarding python unicode string return True @@ -213,8 +231,8 @@ class Util: b = [] for c in mac_str: if i == 2: - if c != ':': - raise MyError('not a valid MAC address: "%s"' % (mac_str)) + if c != ":": + raise MyError("not a valid MAC address: '%s'" % (mac_str)) i = 0 continue try: @@ -222,27 +240,29 @@ class Util: n = int(c, 16) * 16 i = 1 else: - assert(i == 1) + assert i == 1 n = n + int(c, 16) i = 2 b.append(n) - except: - raise MyError('not a valid MAC address: "%s"' % (mac_str)) + except Exception: + raise MyError("not a valid MAC address: '%s'" % (mac_str)) if i == 1: - raise MyError('not a valid MAC address: "%s"' % (mac_str)) + raise MyError("not a valid MAC address: '%s'" % (mac_str)) if force_len is not None: if force_len != len(b): - raise MyError('not a valid MAC address of length %s: "%s"' % (force_len, mac_str)) + raise MyError( + "not a valid MAC address of length %s: '%s'" % (force_len, mac_str) + ) return b @staticmethod def mac_ntoa(mac): if mac is None: return None - return ':'.join(['%02x' % c for c in mac]) + return ":".join(["%02x" % c for c in mac]) @staticmethod - def mac_norm(mac_str, force_len = None): + def mac_norm(mac_str, force_len=None): return Util.mac_ntoa(Util.mac_aton(mac_str, force_len)) @staticmethod @@ -253,12 +273,12 @@ class Util: if isinstance(arg, Util.STRING_TYPE): arg = arg.lower() - if arg in ['y', 'yes', 'on', '1', 'true', 1, True]: + if arg in ["y", "yes", "on", "1", "true", 1, True]: return True - if arg in ['n', 'no', 'off', '0', 'false', 0, False]: + if arg in ["n", "no", "off", "0", "false", 0, False]: return False - raise MyError('value "%s" is not a boolean' % (arg0)) + raise MyError("value '%s' is not a boolean" % (arg0)) @staticmethod def parse_ip(addr, family=None): @@ -273,7 +293,7 @@ class Util: try: a = socket.inet_pton(socket.AF_INET, addr) family = socket.AF_INET - except: + except Exception: a = socket.inet_pton(socket.AF_INET6, addr) family = socket.AF_INET6 return (socket.inet_ntop(family, a), family) @@ -281,17 +301,17 @@ class Util: @staticmethod def addr_family_check(family): if family != socket.AF_INET and family != socket.AF_INET6: - raise MyError('invalid address family %s' % (family)) + raise MyError("invalid address family %s" % (family)) @staticmethod def addr_family_to_v(family): if family is None: - return '' + return "" if family == socket.AF_INET: - return 'v4' + return "v4" if family == socket.AF_INET6: - return 'v6' - raise MyError('invalid address family "%s"' % (family)) + return "v6" + raise MyError("invalid address family '%s'" % (family)) @staticmethod def addr_family_default_prefix(family): @@ -311,61 +331,59 @@ class Util: return prefix >= 0 and prefix <= m @staticmethod - def parse_address(address, family = None): + def parse_address(address, family=None): try: parts = address.split() - addr_parts = parts[0].split('/') + addr_parts = parts[0].split("/") if len(addr_parts) != 2: - raise MyError('expect two addr-parts: ADDR/PLEN') + raise MyError("expect two addr-parts: ADDR/PLEN") a, family = Util.parse_ip(addr_parts[0], family) prefix = int(addr_parts[1]) if not Util.addr_family_valid_prefix(family, prefix): - raise MyError('invalid prefix %s' % (prefix)) + raise MyError("invalid prefix %s" % (prefix)) if len(parts) > 1: - raise MyError('too many parts') - return { - 'address': a, - 'family': family, - 'prefix': prefix, - } - except Exception as e: - raise MyError('invalid address "%s"' % (address)) + raise MyError("too many parts") + return {"address": a, "family": family, "prefix": prefix} + except Exception: + raise MyError("invalid address '%s'" % (address)) + ############################################################################### -class SysUtil: +class SysUtil: @staticmethod def _sysctl_read(filename): try_count = 0 while True: try_count += 1 try: - with open(filename, 'r') as f: + with open(filename, "r") as f: return f.read() - except Exception as e: + except Exception: if try_count < 5: continue raise @staticmethod def _link_read_ifindex(ifname): - c = SysUtil._sysctl_read('/sys/class/net/' + ifname + '/ifindex') + c = SysUtil._sysctl_read("/sys/class/net/" + ifname + "/ifindex") return int(c.strip()) @staticmethod def _link_read_address(ifname): - c = SysUtil._sysctl_read('/sys/class/net/' + ifname + '/address') + c = SysUtil._sysctl_read("/sys/class/net/" + ifname + "/address") return Util.mac_norm(c.strip()) @staticmethod def _link_read_permaddress(ifname): try: - out = Util.check_output(['ethtool', '-P', ifname]) - except MyError as e: + out = Util.check_output(["ethtool", "-P", ifname]) + except MyError: return None import re - m = re.match('^Permanent address: ([0-9A-Fa-f:]*)\n$', out) + + m = re.match("^Permanent address: ([0-9A-Fa-f:]*)\n$", out) if not m: return None return Util.mac_norm(m.group(1)) @@ -373,120 +391,133 @@ class SysUtil: @staticmethod def _link_infos_fetch(): links = {} - for ifname in os.listdir('/sys/class/net/'): - if not os.path.islink('/sys/class/net/' + ifname): + for ifname in os.listdir("/sys/class/net/"): + if not os.path.islink("/sys/class/net/" + ifname): # /sys/class/net may contain certain entries that are not # interface names, like 'bonding_master'. Skip over files # that are not links. continue links[ifname] = { - 'ifindex': SysUtil._link_read_ifindex(ifname), - 'ifname': ifname, - 'address': SysUtil._link_read_address(ifname), - 'perm-address': SysUtil._link_read_permaddress(ifname), + "ifindex": SysUtil._link_read_ifindex(ifname), + "ifname": ifname, + "address": SysUtil._link_read_address(ifname), + "perm-address": SysUtil._link_read_permaddress(ifname), } return links @classmethod def link_infos(cls, refresh=False): if refresh: - l = None + linkinfos = None else: - l = getattr(cls, '_link_infos', None) - if l is None: + linkinfos = getattr(cls, "_link_infos", None) + if linkinfos is None: try_count = 0 b = None while True: try_count += 1 try: # there is a race in that we lookup properties by ifname - # and interfaces can be renamed. Try to avoid that by fetching - # the info twice and repeat until we get the same result. + # and interfaces can be renamed. Try to avoid that by + # fetching the info twice and repeat until we get the same + # result. if b is None: b = SysUtil._link_infos_fetch() - l = SysUtil._link_infos_fetch() - if l != b: - b = l - raise Exception('cannot read stable link-infos. They keep changing') - except: + linkinfos = SysUtil._link_infos_fetch() + if linkinfos != b: + b = linkinfos + raise Exception( + "cannot read stable link-infos. They keep changing" + ) + except Exception: if try_count < 50: raise continue break - cls._link_infos = l - return l + cls._link_infos = linkinfos + return linkinfos @classmethod - def link_info_find(cls, refresh = False, mac = None, ifname = None): + def link_info_find(cls, refresh=False, mac=None, ifname=None): if mac is not None: mac = Util.mac_norm(mac) for li in cls.link_infos(refresh).values(): - if mac is not None and mac not in [li.get('perm-address', None), li.get('address', None)]: + if mac is not None and mac not in [ + li.get("perm-address", None), + li.get("address", None), + ]: continue - if ifname is not None and ifname != li.get('ifname', None): + if ifname is not None and ifname != li.get("ifname", None): continue return li return None + ############################################################################### + class ArgUtil: @staticmethod - def connection_find_by_name(name, connections, n_connections = None): + def connection_find_by_name(name, connections, n_connections=None): if not name: - raise ValueError('missing name argument') + raise ValueError("missing name argument") c = None for idx, connection in enumerate(connections): if n_connections is not None and idx >= n_connections: break - if 'name' not in connection or name != connection['name']: + if "name" not in connection or name != connection["name"]: continue - if connection['state'] == 'absent': + if connection["state"] == "absent": c = None - elif 'type' in connection: - assert connection['state'] in ['up', 'present'] + elif "type" in connection: + assert connection["state"] in ["up", "present"] c = connection return c @staticmethod - def connection_find_master(name, connections, n_connections = None): + def connection_find_master(name, connections, n_connections=None): c = ArgUtil.connection_find_by_name(name, connections, n_connections) if not c: - raise MyError('invalid master/parent "%s"' % (name)) - if c['interface_name'] is None: - raise MyError('invalid master/parent "%s" which needs an "interface_name"' % (name)) - if not Util.ifname_valid(c['interface_name']): - raise MyError('invalid master/parent "%s" which has not a valid "interface_name" ("%s")' % (name, c['interface_name'])) - return c['interface_name'] + raise MyError("invalid master/parent '%s'" % (name)) + if c["interface_name"] is None: + raise MyError( + "invalid master/parent '%s' which needs an 'interface_name'" % (name) + ) + if not Util.ifname_valid(c["interface_name"]): + raise MyError( + 'invalid master/parent \'%s\' with invalid "interface_name" ("%s")' + % (name, c["interface_name"]) + ) + return c["interface_name"] @staticmethod - def connection_find_master_uuid(name, connections, n_connections = None): + def connection_find_master_uuid(name, connections, n_connections=None): c = ArgUtil.connection_find_by_name(name, connections, n_connections) if not c: - raise MyError('invalid master/parent "%s"' % (name)) - return c['nm.uuid'] + raise MyError("invalid master/parent '%s'" % (name)) + return c["nm.uuid"] @staticmethod def connection_get_non_absent_names(connections): # @idx is the index with state['absent']. This will # return the names of all explicitly mentioned profiles. # That is, the names of profiles that should not be deleted. - l = set() + result = set() for connection in connections: - if 'name' not in connection: + if "name" not in connection: continue - if not connection['name']: + if not connection["name"]: continue - l.add(connection['name']) - return l + result.add(connection["name"]) + return result class ArgValidator: MISSING = object() DEFAULT_SENTINEL = object() - def __init__(self, name = None, required = False, default_value = None): + def __init__(self, name=None, required=False, default_value=None): self.name = name self.required = required self.default_value = default_value @@ -494,41 +525,72 @@ class ArgValidator: def get_default_value(self): try: return self.default_value() - except: + except Exception: # pylint: disable=broad-except return self.default_value - def validate(self, value, name = None): - name = name or self.name or '' - v = self._validate(value, name) - return self._validate_post(value, name, v) + def _validate(self, value, name): + raise NotImplementedError() + def validate(self, value, name=None): + name = name or self.name or "" + validated = self._validate(value, name) + return self._validate_post(value, name, validated) + + # pylint: disable=unused-argument,no-self-use def _validate_post(self, value, name, result): return result + class ArgValidatorStr(ArgValidator): - def __init__(self, name, required = False, default_value = None, enum_values = None, allow_empty = False): + def __init__( # pylint: disable=too-many-arguments + self, + name, + required=False, + default_value=None, + enum_values=None, + allow_empty=False, + ): ArgValidator.__init__(self, name, required, default_value) self.enum_values = enum_values self.allow_empty = allow_empty + def _validate(self, value, name): if not isinstance(value, Util.STRING_TYPE): - raise ValidationError(name, 'must be a string but is "%s"' % (value)) - v = str(value) - if self.enum_values is not None and v not in self.enum_values: - raise ValidationError(name, 'is "%s" but must be one of "%s"' % (value, '" "'.join(sorted(self.enum_values)))) - if not self.allow_empty and not v: - raise ValidationError(name, 'cannot be empty') - return v + raise ValidationError(name, "must be a string but is '%s'" % (value)) + value = str(value) + if self.enum_values is not None and value not in self.enum_values: + raise ValidationError( + name, + "is '%s' but must be one of '%s'" + % (value, "' '".join(sorted(self.enum_values))), + ) + if not self.allow_empty and not value: + raise ValidationError(name, "cannot be empty") + return value + class ArgValidatorNum(ArgValidator): - def __init__(self, name, required = False, val_min = None, val_max = None, - default_value = ArgValidator.DEFAULT_SENTINEL, - numeric_type = int): - ArgValidator.__init__(self, name, required, \ - numeric_type(0) if default_value is ArgValidator.DEFAULT_SENTINEL else default_value) + def __init__( # pylint: disable=too-many-arguments + self, + name, + required=False, + val_min=None, + val_max=None, + default_value=ArgValidator.DEFAULT_SENTINEL, + numeric_type=int, + ): + ArgValidator.__init__( + self, + name, + required, + numeric_type(0) + if default_value is ArgValidator.DEFAULT_SENTINEL + else default_value, + ) self.val_min = val_min self.val_max = val_max self.numeric_type = numeric_type + def _validate(self, value, name): v = None try: @@ -536,569 +598,788 @@ class ArgValidatorNum(ArgValidator): v = value else: v2 = self.numeric_type(value) - if isinstance(value, Util.STRING_TYPE) \ - or v2 == value: + if isinstance(value, Util.STRING_TYPE) or v2 == value: v = v2 - except: + except Exception: pass if v is None: - raise ValidationError(name, 'must be an integer number but is "%s"' % (value)) + raise ValidationError( + name, "must be an integer number but is '%s'" % (value) + ) if self.val_min is not None and v < self.val_min: - raise ValidationError(name, 'value is %s but cannot be less then %s' % (value, self.val_min)) + raise ValidationError( + name, "value is %s but cannot be less then %s" % (value, self.val_min) + ) if self.val_max is not None and v > self.val_max: - raise ValidationError(name, 'value is %s but cannot be greater then %s' % (value, self.val_max)) + raise ValidationError( + name, + "value is %s but cannot be greater then %s" % (value, self.val_max), + ) return v + class ArgValidatorBool(ArgValidator): - def __init__(self, name, required = False, default_value = False): + def __init__(self, name, required=False, default_value=False): ArgValidator.__init__(self, name, required, default_value) + def _validate(self, value, name): try: if isinstance(value, bool): return value if isinstance(value, Util.STRING_TYPE) or isinstance(value, int): return Util.boolean(value) - except: + except Exception: pass - raise ValidationError(name, 'must be an boolean but is "%s"' % (value)) + raise ValidationError(name, "must be an boolean but is '%s'" % (value)) + class ArgValidatorDict(ArgValidator): - def __init__(self, name = None, required = False, nested = None, default_value = None, all_missing_during_validate = False): + def __init__( + self, + name=None, + required=False, + nested=None, + default_value=None, + all_missing_during_validate=False, + ): ArgValidator.__init__(self, name, required, default_value) if nested is not None: self.nested = dict([(v.name, v) for v in nested]) else: self.nested = {} self.all_missing_during_validate = all_missing_during_validate + def _validate(self, value, name): result = {} seen_keys = set() try: - l = list(value.items()) - except: - raise ValidationError(name, 'invalid content is not a dictionary') - for (k,v) in l: + items = list(value.items()) + except AttributeError: + raise ValidationError(name, "invalid content is not a dictionary") + for (k, v) in items: if k in seen_keys: - raise ValidationError(name, 'duplicate key "%s"' % (k)) + raise ValidationError(name, "duplicate key '%s'" % (k)) seen_keys.add(k) validator = self.nested.get(k, None) if validator is None: - raise ValidationError(name, 'invalid key "%s"' % (k)) + raise ValidationError(name, "invalid key '%s'" % (k)) try: - vv = validator.validate(v, name + '.' + k) + vv = validator.validate(v, name + "." + k) except ValidationError as e: raise ValidationError(e.name, e.error_message) result[k] = vv - for (k,v) in self.nested.items(): + for (k, v) in self.nested.items(): if k in seen_keys: continue if v.required: - raise ValidationError(name, 'missing required key "%s"' % (k)) + raise ValidationError(name, "missing required key '%s'" % (k)) vv = v.get_default_value() if not self.all_missing_during_validate and vv is not ArgValidator.MISSING: result[k] = vv return result + class ArgValidatorList(ArgValidator): - def __init__(self, name, nested, default_value = None): - ArgValidator.__init__(self, name, required = False, default_value = default_value) + def __init__(self, name, nested, default_value=None): + ArgValidator.__init__(self, name, required=False, default_value=default_value) self.nested = nested - def construct_name(self, name): - return ('' if n is None else n) + name + def _validate(self, value, name): if isinstance(value, Util.STRING_TYPE): # we expect a list. However, for convenience allow to # specify a string, separated by space. Escaping is # not supported. If you need that, define a proper list. - value = [s for s in value.split(' ') if s] + value = [s for s in value.split(" ") if s] result = [] for (idx, v) in enumerate(value): try: - vv = self.nested.validate(v, name + '[' + str(idx) + ']') + vv = self.nested.validate(v, name + "[" + str(idx) + "]") except ValidationError as e: raise ValidationError(e.name, e.error_message) result.append(vv) return result + class ArgValidatorIP(ArgValidatorStr): - def __init__(self, name, family = None, required = False, default_value = None, plain_address = True): + def __init__( + self, name, family=None, required=False, default_value=None, plain_address=True + ): ArgValidatorStr.__init__(self, name, required, default_value, None) self.family = family self.plain_address = plain_address + def _validate(self, value, name): v = ArgValidatorStr._validate(self, value, name) try: addr, family = Util.parse_ip(v, self.family) - except: - raise ValidationError(name, 'value "%s" is not a valid IP%s address' % (value, Util.addr_family_to_v(self.family))) + except Exception: + raise ValidationError( + name, + "value '%s' is not a valid IP%s address" + % (value, Util.addr_family_to_v(self.family)), + ) if self.plain_address: return addr - return { 'family': family, 'address': addr } + return {"family": family, "address": addr} + class ArgValidatorMac(ArgValidatorStr): - def __init__(self, name, force_len = None, required = False, default_value = None): + def __init__(self, name, force_len=None, required=False, default_value=None): ArgValidatorStr.__init__(self, name, required, default_value, None) self.force_len = force_len + def _validate(self, value, name): v = ArgValidatorStr._validate(self, value, name) try: addr = Util.mac_aton(v, self.force_len) - except MyError as e: - raise ValidationError(name, 'value "%s" is not a valid MAC address' % (value)) + except MyError: + raise ValidationError( + name, "value '%s' is not a valid MAC address" % (value) + ) if not addr: - raise ValidationError(name, 'value "%s" is not a valid MAC address' % (value)) + raise ValidationError( + name, "value '%s' is not a valid MAC address" % (value) + ) return Util.mac_ntoa(addr) + class ArgValidatorIPAddr(ArgValidatorDict): - def __init__(self, name, family = None, required = False, default_value = None): - ArgValidatorDict.__init__(self, + def __init__(self, name, family=None, required=False, default_value=None): + ArgValidatorDict.__init__( + self, name, required, - nested = [ - ArgValidatorIP ('address', family = family, required = True, plain_address = False), - ArgValidatorNum('prefix', default_value = None, val_min = 0), + nested=[ + ArgValidatorIP( + "address", family=family, required=True, plain_address=False + ), + ArgValidatorNum("prefix", default_value=None, val_min=0), ], ) self.family = family + def _validate(self, value, name): if isinstance(value, Util.STRING_TYPE): v = str(value) if not v: - raise ValidationError(name, 'cannot be empty') + raise ValidationError(name, "cannot be empty") try: return Util.parse_address(v, self.family) - except: - raise ValidationError(name, 'value "%s" is not a valid IP%s address with prefix length' % (value, Util.addr_family_to_v(self.family))) + except Exception: + raise ValidationError( + name, + "value '%s' is not a valid IP%s address with prefix length" + % (value, Util.addr_family_to_v(self.family)), + ) v = ArgValidatorDict._validate(self, value, name) return { - 'address': v['address']['address'], - 'family': v['address']['family'], - 'prefix': v['prefix'], + "address": v["address"]["address"], + "family": v["address"]["family"], + "prefix": v["prefix"], } + def _validate_post(self, value, name, result): - family = result['family'] - prefix = result['prefix'] + family = result["family"] + prefix = result["prefix"] if prefix is None: prefix = Util.addr_family_default_prefix(family) - result['prefix'] = prefix + result["prefix"] = prefix elif not Util.addr_family_valid_prefix(family, prefix): - raise ValidationError(name, 'invalid prefix %s in "%s"' % (prefix, value)) + raise ValidationError(name, "invalid prefix %s in '%s'" % (prefix, value)) return result + class ArgValidatorIPRoute(ArgValidatorDict): - def __init__(self, name, family = None, required = False, default_value = None): - ArgValidatorDict.__init__(self, + def __init__(self, name, family=None, required=False, default_value=None): + ArgValidatorDict.__init__( + self, name, required, - nested = [ - ArgValidatorIP ('network', family = family, required = True, plain_address = False), - ArgValidatorNum('prefix', default_value = None, val_min = 0), - ArgValidatorIP ('gateway', family = family, default_value = None, plain_address = False), - ArgValidatorNum('metric', default_value = -1, val_min = -1, val_max = 0xFFFFFFFF), + nested=[ + ArgValidatorIP( + "network", family=family, required=True, plain_address=False + ), + ArgValidatorNum("prefix", default_value=None, val_min=0), + ArgValidatorIP( + "gateway", family=family, default_value=None, plain_address=False + ), + ArgValidatorNum( + "metric", default_value=-1, val_min=-1, val_max=0xFFFFFFFF + ), ], ) self.family = family + def _validate_post(self, value, name, result): - network = result['network'] + network = result["network"] - family = network['family'] - result['network'] = network['address'] - result['family'] = family + family = network["family"] + result["network"] = network["address"] + result["family"] = family - gateway = result['gateway'] + gateway = result["gateway"] if gateway is not None: - if family != gateway['family']: - raise ValidationError(name, 'conflicting address family between network and gateway \"%s\"' % (gateway['address'])) - result['gateway'] = gateway['address'] + if family != gateway["family"]: + raise ValidationError( + name, + "conflicting address family between network and gateway '%s'" + % (gateway["address"]), + ) + result["gateway"] = gateway["address"] - prefix = result['prefix'] + prefix = result["prefix"] if prefix is None: prefix = Util.addr_family_default_prefix(family) - result['prefix'] = prefix + result["prefix"] = prefix elif not Util.addr_family_valid_prefix(family, prefix): - raise ValidationError(name, 'invalid prefix %s in "%s"' % (prefix, value)) + raise ValidationError(name, "invalid prefix %s in '%s'" % (prefix, value)) return result + class ArgValidator_DictIP(ArgValidatorDict): def __init__(self): - ArgValidatorDict.__init__(self, - name = 'ip', - nested = [ - ArgValidatorBool('dhcp4', default_value = None), - ArgValidatorBool('dhcp4_send_hostname', default_value = None), - ArgValidatorIP ('gateway4', family = socket.AF_INET), - ArgValidatorNum ('route_metric4', val_min = -1, val_max = 0xFFFFFFFF, default_value = None), - ArgValidatorBool('auto6', default_value = None), - ArgValidatorIP ('gateway6', family = socket.AF_INET6), - ArgValidatorNum ('route_metric6', val_min = -1, val_max = 0xFFFFFFFF, default_value = None), - ArgValidatorList('address', - nested = ArgValidatorIPAddr('address[?]'), - default_value = list, + ArgValidatorDict.__init__( + self, + name="ip", + nested=[ + ArgValidatorBool("dhcp4", default_value=None), + ArgValidatorBool("dhcp4_send_hostname", default_value=None), + ArgValidatorIP("gateway4", family=socket.AF_INET), + ArgValidatorNum( + "route_metric4", val_min=-1, val_max=0xFFFFFFFF, default_value=None ), - ArgValidatorList('route', - nested = ArgValidatorIPRoute('route[?]'), - default_value = list, + ArgValidatorBool("auto6", default_value=None), + ArgValidatorIP("gateway6", family=socket.AF_INET6), + ArgValidatorNum( + "route_metric6", val_min=-1, val_max=0xFFFFFFFF, default_value=None ), - ArgValidatorBool('route_append_only'), - ArgValidatorBool('rule_append_only'), - ArgValidatorList('dns', - nested = ArgValidatorIP('dns[?]', plain_address=False), - default_value = list, + ArgValidatorList( + "address", + nested=ArgValidatorIPAddr("address[?]"), + default_value=list, ), - ArgValidatorList('dns_search', - nested = ArgValidatorStr('dns_search[?]'), - default_value = list, + ArgValidatorList( + "route", nested=ArgValidatorIPRoute("route[?]"), default_value=list + ), + ArgValidatorBool("route_append_only"), + ArgValidatorBool("rule_append_only"), + ArgValidatorList( + "dns", + nested=ArgValidatorIP("dns[?]", plain_address=False), + default_value=list, + ), + ArgValidatorList( + "dns_search", + nested=ArgValidatorStr("dns_search[?]"), + default_value=list, ), ], - default_value = lambda: { - 'dhcp4': True, - 'dhcp4_send_hostname': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'gateway6': None, - 'route_metric6': None, - 'address': [], - 'route': [], - 'route_append_only': False, - 'rule_append_only': False, - 'dns': [], - 'dns_search': [], + default_value=lambda: { + "dhcp4": True, + "dhcp4_send_hostname": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "gateway6": None, + "route_metric6": None, + "address": [], + "route": [], + "route_append_only": False, + "rule_append_only": False, + "dns": [], + "dns_search": [], }, ) def _validate_post(self, value, name, result): - if result['dhcp4'] is None: - result['dhcp4'] = result['dhcp4_send_hostname'] is not None or not any([a for a in result['address'] if a['family'] == socket.AF_INET]) - if result['auto6'] is None: - result['auto6'] = not any([a for a in result['address'] if a['family'] == socket.AF_INET6]) - if result['dhcp4_send_hostname'] is not None: - if not result['dhcp4']: - raise ValidationError(name, '"dhcp4_send_hostname" is only valid if "dhcp4" is enabled') + if result["dhcp4"] is None: + result["dhcp4"] = result["dhcp4_send_hostname"] is not None or not any( + [a for a in result["address"] if a["family"] == socket.AF_INET] + ) + if result["auto6"] is None: + result["auto6"] = not any( + [a for a in result["address"] if a["family"] == socket.AF_INET6] + ) + if result["dhcp4_send_hostname"] is not None: + if not result["dhcp4"]: + raise ValidationError( + name, "'dhcp4_send_hostname' is only valid if 'dhcp4' is enabled" + ) return result + class ArgValidator_DictEthernet(ArgValidatorDict): def __init__(self): - ArgValidatorDict.__init__(self, - name = 'ethernet', - nested = [ - ArgValidatorBool('autoneg', default_value = None), - ArgValidatorNum ('speed', val_min = 0, val_max = 0xFFFFFFFF, default_value = 0), - ArgValidatorStr ('duplex', enum_values = ['half', 'full']), + ArgValidatorDict.__init__( + self, + name="ethernet", + nested=[ + ArgValidatorBool("autoneg", default_value=None), + ArgValidatorNum( + "speed", val_min=0, val_max=0xFFFFFFFF, default_value=0 + ), + ArgValidatorStr("duplex", enum_values=["half", "full"]), ], - default_value = ArgValidator.MISSING, + default_value=ArgValidator.MISSING, ) def get_default_ethernet(self): - return { - 'autoneg': None, - 'speed': 0, - 'duplex': None, - } + return {"autoneg": None, "speed": 0, "duplex": None} def _validate_post(self, value, name, result): - has_speed_or_duplex = result['speed'] != 0 \ - or result['duplex'] is not None - if result['autoneg'] is None: + has_speed_or_duplex = result["speed"] != 0 or result["duplex"] is not None + if result["autoneg"] is None: if has_speed_or_duplex: - result['autoneg'] = False - elif result['autoneg']: + result["autoneg"] = False + elif result["autoneg"]: if has_speed_or_duplex: - raise ValidationError(name, 'cannot specify "%s" with "autoneg" enabled' % ('duplex' if result['duplex'] is not None else 'speed')) + raise ValidationError( + name, + "cannot specify '%s' with 'autoneg' enabled" + % ("duplex" if result["duplex"] is not None else "speed"), + ) else: if not has_speed_or_duplex: - raise ValidationError(name, 'need to specify "duplex" and "speed" with "autoneg" enabled') - if ( has_speed_or_duplex \ - and ( result['speed'] == 0 \ - or result['duplex'] is None)): - raise ValidationError(name, 'need to specify both "speed" and "duplex" with "autoneg" disabled') + raise ValidationError( + name, + 'need to specify \'duplex\' and "speed" with "autoneg" enabled', + ) + if has_speed_or_duplex and (result["speed"] == 0 or result["duplex"] is None): + raise ValidationError( + name, + 'need to specify both \'speed\' and "duplex" with "autoneg" disabled', + ) return result + class ArgValidator_DictBond(ArgValidatorDict): - VALID_MODES = [ 'balance-rr', 'active-backup', 'balance-xor', 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb'] + VALID_MODES = [ + "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + ] def __init__(self): - ArgValidatorDict.__init__(self, - name = 'bond', - nested = [ - ArgValidatorStr ('mode', enum_values = ArgValidator_DictBond.VALID_MODES), - ArgValidatorNum ('miimon', val_min = 0, val_max = 1000000, default_value = None), + ArgValidatorDict.__init__( + self, + name="bond", + nested=[ + ArgValidatorStr("mode", enum_values=ArgValidator_DictBond.VALID_MODES), + ArgValidatorNum( + "miimon", val_min=0, val_max=1000000, default_value=None + ), ], - default_value = ArgValidator.MISSING, + default_value=ArgValidator.MISSING, ) def get_default_bond(self): - return { - 'mode': ArgValidator_DictBond.VALID_MODES[0], - 'miimon': None, - } + return {"mode": ArgValidator_DictBond.VALID_MODES[0], "miimon": None} + class ArgValidator_DictInfiniband(ArgValidatorDict): - def __init__(self): - ArgValidatorDict.__init__(self, - name = 'infiniband', - nested = [ - ArgValidatorStr ('transport_mode', enum_values = ['datagram', 'connected']), - ArgValidatorNum ('p_key', val_min = -1, val_max = 0xFFFF, default_value = -1), + ArgValidatorDict.__init__( + self, + name="infiniband", + nested=[ + ArgValidatorStr( + "transport_mode", enum_values=["datagram", "connected"] + ), + ArgValidatorNum("p_key", val_min=-1, val_max=0xFFFF, default_value=-1), ], - default_value = ArgValidator.MISSING, + default_value=ArgValidator.MISSING, ) def get_default_infiniband(self): - return { - 'transport_mode': 'datagram', - 'p_key': -1, - } + return {"transport_mode": "datagram", "p_key": -1} + class ArgValidator_DictVlan(ArgValidatorDict): - def __init__(self): - ArgValidatorDict.__init__(self, - name = 'vlan', - nested = [ - ArgValidatorNum ('id', val_min = 0, val_max = 4094, required = True), - ], - default_value = ArgValidator.MISSING, + ArgValidatorDict.__init__( + self, + name="vlan", + nested=[ArgValidatorNum("id", val_min=0, val_max=4094, required=True)], + default_value=ArgValidator.MISSING, ) def get_default_vlan(self): - return { - 'id': None, - } + return {"id": None} + class ArgValidator_DictMacvlan(ArgValidatorDict): - VALID_MODES = ['vepa', 'bridge', 'private', 'passthru', 'source'] + VALID_MODES = ["vepa", "bridge", "private", "passthru", "source"] def __init__(self): - ArgValidatorDict.__init__(self, - name = 'macvlan', - nested = [ - ArgValidatorStr ('mode', enum_values = ArgValidator_DictMacvlan.VALID_MODES, default_value = 'bridge'), - ArgValidatorBool('promiscuous', default_value = True), - ArgValidatorBool('tap', default_value = False), + ArgValidatorDict.__init__( + self, + name="macvlan", + nested=[ + ArgValidatorStr( + "mode", + enum_values=ArgValidator_DictMacvlan.VALID_MODES, + default_value="bridge", + ), + ArgValidatorBool("promiscuous", default_value=True), + ArgValidatorBool("tap", default_value=False), ], - default_value = ArgValidator.MISSING, + default_value=ArgValidator.MISSING, ) def get_default_macvlan(self): - return { - 'mode': 'bridge', - 'promiscuous': True, - 'tap': False, - } + return {"mode": "bridge", "promiscuous": True, "tap": False} def _validate_post(self, value, name, result): - if result['promiscuous'] == False and result['mode'] != "passthru": - raise ValidationError(name, 'non promiscuous operation is allowed only in passthru mode') + if result["promiscuous"] is False and result["mode"] != "passthru": + raise ValidationError( + name, "non promiscuous operation is allowed only in passthru mode" + ) return result + class ArgValidator_DictConnection(ArgValidatorDict): - VALID_STATES = ['up', 'down', 'present', 'absent', 'wait'] - VALID_TYPES = [ 'ethernet', 'infiniband', 'bridge', 'team', 'bond', 'vlan', 'macvlan' ] - VALID_SLAVE_TYPES = [ 'bridge', 'bond', 'team' ] + VALID_STATES = ["up", "down", "present", "absent", "wait"] + VALID_TYPES = [ + "ethernet", + "infiniband", + "bridge", + "team", + "bond", + "vlan", + "macvlan", + ] + VALID_SLAVE_TYPES = ["bridge", "bond", "team"] def __init__(self): - ArgValidatorDict.__init__(self, - name = 'connections[?]', - nested = [ - ArgValidatorStr ('name'), - ArgValidatorStr ('state', enum_values = ArgValidator_DictConnection.VALID_STATES), - ArgValidatorBool('force_state_change', default_value = None), - ArgValidatorNum ('wait', val_min = 0, val_max = 3600, numeric_type = float), - ArgValidatorStr ('type', enum_values = ArgValidator_DictConnection.VALID_TYPES), - ArgValidatorBool('autoconnect', default_value = True), - ArgValidatorStr ('slave_type', enum_values = ArgValidator_DictConnection.VALID_SLAVE_TYPES), - ArgValidatorStr ('master'), - ArgValidatorStr ('interface_name'), - ArgValidatorMac ('mac'), - ArgValidatorNum ('mtu', val_min = 0, val_max = 0xFFFFFFFF, default_value = None), - ArgValidatorStr ('zone'), - ArgValidatorBool('check_iface_exists', default_value = True), - ArgValidatorStr ('parent'), - ArgValidatorBool('ignore_errors', default_value = None), + ArgValidatorDict.__init__( + self, + name="connections[?]", + nested=[ + ArgValidatorStr("name"), + ArgValidatorStr( + "state", enum_values=ArgValidator_DictConnection.VALID_STATES + ), + ArgValidatorBool("force_state_change", default_value=None), + ArgValidatorNum("wait", val_min=0, val_max=3600, numeric_type=float), + ArgValidatorStr( + "type", enum_values=ArgValidator_DictConnection.VALID_TYPES + ), + ArgValidatorBool("autoconnect", default_value=True), + ArgValidatorStr( + "slave_type", + enum_values=ArgValidator_DictConnection.VALID_SLAVE_TYPES, + ), + ArgValidatorStr("master"), + ArgValidatorStr("interface_name"), + ArgValidatorMac("mac"), + ArgValidatorNum( + "mtu", val_min=0, val_max=0xFFFFFFFF, default_value=None + ), + ArgValidatorStr("zone"), + ArgValidatorBool("check_iface_exists", default_value=True), + ArgValidatorStr("parent"), + ArgValidatorBool("ignore_errors", default_value=None), ArgValidator_DictIP(), ArgValidator_DictEthernet(), ArgValidator_DictBond(), ArgValidator_DictInfiniband(), ArgValidator_DictVlan(), ArgValidator_DictMacvlan(), - # deprecated options: - ArgValidatorStr ('infiniband_transport_mode', enum_values = ['datagram', 'connected'], default_value = ArgValidator.MISSING), - ArgValidatorNum ('infiniband_p_key', val_min = -1, val_max = 0xFFFF, default_value = ArgValidator.MISSING), - ArgValidatorNum ('vlan_id', val_min = 0, val_max = 4094, default_value = ArgValidator.MISSING), + ArgValidatorStr( + "infiniband_transport_mode", + enum_values=["datagram", "connected"], + default_value=ArgValidator.MISSING, + ), + ArgValidatorNum( + "infiniband_p_key", + val_min=-1, + val_max=0xFFFF, + default_value=ArgValidator.MISSING, + ), + ArgValidatorNum( + "vlan_id", + val_min=0, + val_max=4094, + default_value=ArgValidator.MISSING, + ), ], - default_value = dict, - all_missing_during_validate = True, + default_value=dict, + all_missing_during_validate=True, ) def _validate_post(self, value, name, result): - if 'state' not in result: - if 'type' in result: - result['state'] = 'present' - elif list(result.keys()) == [ 'wait' ]: - result['state'] = 'wait' + if "state" not in result: + if "type" in result: + result["state"] = "present" + elif list(result.keys()) == ["wait"]: + result["state"] = "wait" else: - result['state'] = 'up' + result["state"] = "up" - if result['state'] == 'present' or (result['state'] == 'up' and 'type' in result): + if result["state"] == "present" or ( + result["state"] == "up" and "type" in result + ): VALID_FIELDS = list(self.nested.keys()) - if result['state'] == 'present': - VALID_FIELDS.remove('wait') - VALID_FIELDS.remove('force_state_change') - elif result['state'] in ['up', 'down']: - VALID_FIELDS = ['name', 'state', 'wait', 'ignore_errors', 'force_state_change'] - elif result['state'] == 'absent': - VALID_FIELDS = ['name', 'state', 'ignore_errors'] - elif result['state'] == 'wait': - VALID_FIELDS = ['state', 'wait'] + if result["state"] == "present": + VALID_FIELDS.remove("wait") + VALID_FIELDS.remove("force_state_change") + elif result["state"] in ["up", "down"]: + VALID_FIELDS = [ + "name", + "state", + "wait", + "ignore_errors", + "force_state_change", + ] + elif result["state"] == "absent": + VALID_FIELDS = ["name", "state", "ignore_errors"] + elif result["state"] == "wait": + VALID_FIELDS = ["state", "wait"] else: assert False VALID_FIELDS = set(VALID_FIELDS) for k in result: if k not in VALID_FIELDS: - raise ValidationError(name + '.' + k, 'property is not allowed for state "%s"' % (result['state'])) + raise ValidationError( + name + "." + k, + "property is not allowed for state '%s'" % (result["state"]), + ) - if result['state'] != 'wait': - if result['state'] == 'absent': - if 'name' not in result: - result['name'] = '' # set to empty string to mean *absent all others* + if result["state"] != "wait": + if result["state"] == "absent": + if "name" not in result: + result[ + "name" + ] = "" # set to empty string to mean *absent all others* else: - if 'name' not in result: - raise ValidationError(name, 'missing "name"') + if "name" not in result: + raise ValidationError(name, "missing 'name'") - if result['state'] in [ 'wait', 'up', 'down' ]: - if 'wait' not in result: - result['wait'] = None + if result["state"] in ["wait", "up", "down"]: + if "wait" not in result: + result["wait"] = None else: - if 'wait' in result: - raise ValidationError(name + '.wait', '"wait" is not allowed for state "%s"' % (result['state'])) + if "wait" in result: + raise ValidationError( + name + ".wait", + "'wait' is not allowed for state '%s'" % (result["state"]), + ) - if result['state'] == 'present' and 'type' not in result: - raise ValidationError(name + '.state', '"present" state requires a "type" argument') + if result["state"] == "present" and "type" not in result: + raise ValidationError( + name + ".state", '"present" state requires a "type" argument' + ) - if 'type' in result: + if "type" in result: - if 'master' in result: - if 'slave_type' not in result: - result['slave_type'] = None - if result['master'] == result['name']: - raise ValidationError(name + '.master', '"master" cannot refer to itself') + if "master" in result: + if "slave_type" not in result: + result["slave_type"] = None + if result["master"] == result["name"]: + raise ValidationError( + name + ".master", '"master" cannot refer to itself' + ) else: - if 'slave_type' in result: - raise ValidationError(name + '.slave_type', '"slave_type" requires a "master" property') + if "slave_type" in result: + raise ValidationError( + name + ".slave_type", + "'slave_type' requires a 'master' property", + ) - if 'ip' in result: - if 'master' in result: - raise ValidationError(name + '.ip', 'a slave cannot have an "ip" property') + if "ip" in result: + if "master" in result: + raise ValidationError( + name + ".ip", 'a slave cannot have an "ip" property' + ) else: - if 'master' not in result: - result['ip'] = self.nested['ip'].get_default_value() + if "master" not in result: + result["ip"] = self.nested["ip"].get_default_value() - if 'zone' in result: - if 'master' in result: - raise ValidationError(name + '.zone', '"zone" cannot be configured for slave types') + if "zone" in result: + if "master" in result: + raise ValidationError( + name + ".zone", '"zone" cannot be configured for slave types' + ) else: - result['zone'] = None + result["zone"] = None - if 'mac' in result: - if result['type'] not in [ 'ethernet', 'infiniband' ]: - raise ValidationError(name + '.mac', 'a "mac" address is only allowed for type "ethernet" or "infiniband"') - l = len(Util.mac_aton(result['mac'])) - if result['type'] == 'ethernet' and l != 6: - raise ValidationError(name + '.mac', 'a "mac" address for type ethernet requires 6 octets but is "%s"' % result['mac']) - if result['type'] == 'infiniband' and l != 20: - raise ValidationError(name + '.mac', 'a "mac" address for type ethernet requires 20 octets but is "%s"' % result['mac']) + if "mac" in result: + if result["type"] not in ["ethernet", "infiniband"]: + raise ValidationError( + name + ".mac", + "a 'mac' address is only allowed for type 'ethernet' " + "or 'infiniband'", + ) + maclen = len(Util.mac_aton(result["mac"])) + if result["type"] == "ethernet" and maclen != 6: + raise ValidationError( + name + ".mac", + "a 'mac' address for type ethernet requires 6 octets " + "but is '%s'" % result["mac"], + ) + if result["type"] == "infiniband" and maclen != 20: + raise ValidationError( + name + ".mac", + "a 'mac' address for type ethernet requires 20 octets " + "but is '%s'" % result["mac"], + ) - if result['type'] == 'infiniband': - if 'infiniband' not in result: - result['infiniband'] = self.nested['infiniband'].get_default_infiniband() - if 'infiniband_transport_mode' in result: - result['infiniband']['transport_mode'] = result['infiniband_transport_mode'] - del result['infiniband_transport_mode'] - if 'infiniband_p_key' in result: - result['infiniband']['p_key'] = result['infiniband_p_key'] - del result['infiniband_p_key'] + if result["type"] == "infiniband": + if "infiniband" not in result: + result["infiniband"] = self.nested[ + "infiniband" + ].get_default_infiniband() + if "infiniband_transport_mode" in result: + result["infiniband"]["transport_mode"] = result[ + "infiniband_transport_mode" + ] + del result["infiniband_transport_mode"] + if "infiniband_p_key" in result: + result["infiniband"]["p_key"] = result["infiniband_p_key"] + del result["infiniband_p_key"] else: - if 'infiniband_transport_mode' in result: - raise ValidationError(name + '.infiniband_transport_mode', 'cannot mix deprecated "infiniband_transport_mode" property with "infiniband" settings') - if 'infiniband_p_key' in result: - raise ValidationError(name + '.infiniband_p_key', 'cannot mix deprecated "infiniband_p_key" property with "infiniband" settings') - if result['infiniband']['transport_mode'] is None: - result['infiniband']['transport_mode'] = 'datagram' - if result['infiniband']['p_key'] != -1: - if 'mac' not in result and \ - 'parent' not in result: - raise ValidationError(name + '.infiniband.p_key', 'a infiniband device with "infiniband.p_key" property also needs "mac" or "parent" property') + if "infiniband_transport_mode" in result: + raise ValidationError( + name + ".infiniband_transport_mode", + "cannot mix deprecated 'infiniband_transport_mode' " + "property with 'infiniband' settings", + ) + if "infiniband_p_key" in result: + raise ValidationError( + name + ".infiniband_p_key", + "cannot mix deprecated 'infiniband_p_key' property " + "with 'infiniband' settings", + ) + if result["infiniband"]["transport_mode"] is None: + result["infiniband"]["transport_mode"] = "datagram" + if result["infiniband"]["p_key"] != -1: + if "mac" not in result and "parent" not in result: + raise ValidationError( + name + ".infiniband.p_key", + "a infiniband device with 'infiniband.p_key' " + "property also needs 'mac' or 'parent' property", + ) else: - if 'infiniband' in result: - raise ValidationError(name + '.infiniband', '"infiniband" settings are only allowed for type "infiniband"') - if 'infiniband_transport_mode' in result: - raise ValidationError(name + '.infiniband_transport_mode', 'a "infiniband_transport_mode" property is only allowed for type "infiniband"') - if 'infiniband_p_key' in result: - raise ValidationError(name + '.infiniband_p_key', 'a "infiniband_p_key" property is only allowed for type "infiniband"') + if "infiniband" in result: + raise ValidationError( + name + ".infiniband", + "'infiniband' settings are only allowed for type 'infiniband'", + ) + if "infiniband_transport_mode" in result: + raise ValidationError( + name + ".infiniband_transport_mode", + "a 'infiniband_transport_mode' property is only " + "allowed for type 'infiniband'", + ) + if "infiniband_p_key" in result: + raise ValidationError( + name + ".infiniband_p_key", + "a 'infiniband_p_key' property is only allowed for " + "type 'infiniband'", + ) - if 'interface_name' in result: - if not Util.ifname_valid(result['interface_name']): - raise ValidationError(name + '.interface_name', 'invalid "interface_name" "%s"' % (result['interface_name'])) + if "interface_name" in result: + if not Util.ifname_valid(result["interface_name"]): + raise ValidationError( + name + ".interface_name", + "invalid 'interface_name' '%s'" % (result["interface_name"]), + ) else: - if result['type'] in [ 'bridge', 'bond', 'team', 'vlan', 'macvlan' ]: - if not Util.ifname_valid(result['name']): - raise ValidationError(name + '.interface_name', 'requires "interface_name" as "name" "%s" is not valid' % (result['name'])) - result['interface_name'] = result['name'] + if result["type"] in ["bridge", "bond", "team", "vlan", "macvlan"]: + if not Util.ifname_valid(result["name"]): + raise ValidationError( + name + ".interface_name", + 'requires \'interface_name\' as "name" "%s" is not valid' + % (result["name"]), + ) + result["interface_name"] = result["name"] - if result['type'] == 'vlan': - if 'vlan' not in result: - if 'vlan_id' not in result: - raise ValidationError(name + '.vlan', 'missing "vlan" settings for "type" "vlan"') - result['vlan'] = self.nested['vlan'].get_default_vlan() - result['vlan']['id'] = result['vlan_id'] - del result['vlan_id'] + if result["type"] == "vlan": + if "vlan" not in result: + if "vlan_id" not in result: + raise ValidationError( + name + ".vlan", 'missing "vlan" settings for "type" "vlan"' + ) + result["vlan"] = self.nested["vlan"].get_default_vlan() + result["vlan"]["id"] = result["vlan_id"] + del result["vlan_id"] else: - if 'vlan_id' in result: - raise ValidationError(name + '.vlan_id', 'don\'t use the deprecated "vlan_id" together with the "vlan" settings"') - if 'parent' not in result: - raise ValidationError(name + '.parent', 'missing "parent" for "type" "vlan"') + if "vlan_id" in result: + raise ValidationError( + name + ".vlan_id", + "don't use the deprecated 'vlan_id' together with the " + "'vlan' settings'", + ) + if "parent" not in result: + raise ValidationError( + name + ".parent", 'missing "parent" for "type" "vlan"' + ) else: - if 'vlan' in result: - raise ValidationError(name + '.vlan', '"vlan" is only allowed for "type" "vlan"') - if 'vlan_id' in result: - raise ValidationError(name + '.vlan_id', '"vlan_id" is only allowed for "type" "vlan"') + if "vlan" in result: + raise ValidationError( + name + ".vlan", '"vlan" is only allowed for "type" "vlan"' + ) + if "vlan_id" in result: + raise ValidationError( + name + ".vlan_id", '"vlan_id" is only allowed for "type" "vlan"' + ) - if 'parent' in result: - if result['type'] not in ['vlan', 'macvlan', 'infiniband']: - raise ValidationError(name + '.parent', '"parent" is only allowed for type "vlan", "macvlan" or "infiniband"') - if result['parent'] == result['name']: - raise ValidationError(name + '.parent', '"parent" cannot refer to itself') + if "parent" in result: + if result["type"] not in ["vlan", "macvlan", "infiniband"]: + raise ValidationError( + name + ".parent", + '\'parent\' is only allowed for type "vlan", "macvlan" or ' + "'infiniband'", + ) + if result["parent"] == result["name"]: + raise ValidationError( + name + ".parent", '"parent" cannot refer to itself' + ) - if result['type'] == 'bond': - if 'bond' not in result: - result['bond'] = self.nested['bond'].get_default_bond() + if result["type"] == "bond": + if "bond" not in result: + result["bond"] = self.nested["bond"].get_default_bond() else: - if 'bond' in result: - raise ValidationError(name + '.bond', '"bond" settings are not allowed for "type" "%s"' % (result['type'])) + if "bond" in result: + raise ValidationError( + name + ".bond", + '\'bond\' settings are not allowed for "type" "%s"' + % (result["type"]), + ) - if result['type'] in ['ethernet', 'vlan', 'bridge', 'bond', 'team']: - if 'ethernet' not in result: - result['ethernet'] = self.nested['ethernet'].get_default_ethernet() + if result["type"] in ["ethernet", "vlan", "bridge", "bond", "team"]: + if "ethernet" not in result: + result["ethernet"] = self.nested["ethernet"].get_default_ethernet() else: - if 'ethernet' in result: - raise ValidationError(name + '.ethernet', '"ethernet" settings are not allowed for "type" "%s"' % (result['type'])) + if "ethernet" in result: + raise ValidationError( + name + ".ethernet", + '\'ethernet\' settings are not allowed for "type" "%s"' + % (result["type"]), + ) - if result['type'] == 'macvlan': - if 'macvlan' not in result: - result['macvlan'] = self.nested['macvlan'].get_default_macvlan() + if result["type"] == "macvlan": + if "macvlan" not in result: + result["macvlan"] = self.nested["macvlan"].get_default_macvlan() else: - if 'macvlan' in result: - raise ValidationError(name + '.macvlan', '"macvlan" settings are not allowed for "type" "%s"' % (result['type'])) + if "macvlan" in result: + raise ValidationError( + name + ".macvlan", + '\'macvlan\' settings are not allowed for "type" "%s"' + % (result["type"]), + ) for k in VALID_FIELDS: if k in result: @@ -1110,84 +1391,124 @@ class ArgValidator_DictConnection(ArgValidatorDict): return result + class ArgValidator_ListConnections(ArgValidatorList): def __init__(self): - ArgValidatorList.__init__(self, - name = 'connections', - nested = ArgValidator_DictConnection(), - default_value = list + ArgValidatorList.__init__( + self, + name="connections", + nested=ArgValidator_DictConnection(), + default_value=list, ) def _validate_post(self, value, name, result): for idx, connection in enumerate(result): - if connection['state'] in ['up']: - if connection['state'] == 'up' and 'type' in connection: + if connection["state"] in ["up"]: + if connection["state"] == "up" and "type" in connection: pass - elif not ArgUtil.connection_find_by_name(connection['name'], result, idx): - raise ValidationError(name + '[' + str(idx) + '].name', 'state "%s" references non-existing connection "%s"' % (connection['state'], connection['name'])) - if 'type' in connection: - if connection['master']: - c = ArgUtil.connection_find_by_name(connection['master'], result, idx) + elif not ArgUtil.connection_find_by_name( + connection["name"], result, idx + ): + raise ValidationError( + name + "[" + str(idx) + "].name", + "state '%s' references non-existing connection '%s'" + % (connection["state"], connection["name"]), + ) + if "type" in connection: + if connection["master"]: + c = ArgUtil.connection_find_by_name( + connection["master"], result, idx + ) if not c: - raise ValidationError(name + '[' + str(idx) + '].master', 'references non-existing "master" connection "%s"' % (connection['master'])) - if c['type'] not in ArgValidator_DictConnection.VALID_SLAVE_TYPES: - raise ValidationError(name + '[' + str(idx) + '].master', 'references "master" connection "%s" which is not a master type by "%s"' % (connection['master'], c['type'])) - if connection['slave_type'] is None: - connection['slave_type'] = c['type'] - elif connection['slave_type'] != c['type']: - raise ValidationError(name + '[' + str(idx) + '].master', 'references "master" connection "%s" which is of type "%s" instead of slave_type "%s"' % (connection['master'], c['type'], connection['slave_type'])) - if connection['parent']: - if not ArgUtil.connection_find_by_name(connection['parent'], result, idx): - raise ValidationError(name + '[' + str(idx) + '].parent', 'references non-existing "parent" connection "%s"' % (connection['parent'])) + raise ValidationError( + name + "[" + str(idx) + "].master", + "references non-existing 'master' connection '%s'" + % (connection["master"]), + ) + if c["type"] not in ArgValidator_DictConnection.VALID_SLAVE_TYPES: + raise ValidationError( + name + "[" + str(idx) + "].master", + "references 'master' connection '%s' which is not a master " + "type by '%s'" % (connection["master"], c["type"]), + ) + if connection["slave_type"] is None: + connection["slave_type"] = c["type"] + elif connection["slave_type"] != c["type"]: + raise ValidationError( + name + "[" + str(idx) + "].master", + "references 'master' connection '%s' which is of type '%s' " + "instead of slave_type '%s'" + % ( + connection["master"], + c["type"], + connection["slave_type"], + ), + ) + if connection["parent"]: + if not ArgUtil.connection_find_by_name( + connection["parent"], result, idx + ): + raise ValidationError( + name + "[" + str(idx) + "].parent", + "references non-existing 'parent' connection '%s'" + % (connection["parent"]), + ) return result - VALIDATE_ONE_MODE_NM = 'nm' - VALIDATE_ONE_MODE_INITSCRIPTS = 'initscripts' + VALIDATE_ONE_MODE_NM = "nm" + VALIDATE_ONE_MODE_INITSCRIPTS = "initscripts" def validate_connection_one(self, mode, connections, idx): connection = connections[idx] - if 'type' not in connection: + if "type" not in connection: return - if ( (connection['parent']) - and ( ( (mode == self.VALIDATE_ONE_MODE_INITSCRIPTS) - and (connection['type'] == 'vlan')) - or ( (connection['type'] == 'infiniband') - and (connection['infiniband']['p_key'] != -1)))): + if (connection["parent"]) and ( + ( + (mode == self.VALIDATE_ONE_MODE_INITSCRIPTS) + and (connection["type"] == "vlan") + ) + or ( + (connection["type"] == "infiniband") + and (connection["infiniband"]["p_key"] != -1) + ) + ): try: - ArgUtil.connection_find_master(connection['parent'], connections, idx) - except MyError as e: - raise ValidationError.from_connection(idx, 'profile references a parent "%s" which has \'interface_name\' missing' % (connection['parent'])) + ArgUtil.connection_find_master(connection["parent"], connections, idx) + except MyError: + raise ValidationError.from_connection( + idx, + "profile references a parent '%s' which has 'interface_name' " + "missing" % (connection["parent"]), + ) - if ( (connection['master']) - and (mode == self.VALIDATE_ONE_MODE_INITSCRIPTS)): + if (connection["master"]) and (mode == self.VALIDATE_ONE_MODE_INITSCRIPTS): try: - ArgUtil.connection_find_master(connection['master'], connections, idx) - except MyError as e: - raise ValidationError.from_connection(idx, 'profile references a master "%s" which has \'interface_name\' missing' % (connection['master'])) + ArgUtil.connection_find_master(connection["master"], connections, idx) + except MyError: + raise ValidationError.from_connection( + idx, + "profile references a master '%s' which has 'interface_name' " + "missing" % (connection["master"]), + ) + ############################################################################### + class IfcfgUtil: - FILE_TYPES = [ - 'ifcfg', - 'keys', - 'route', - 'route6', - 'rule', - 'rule6', - ] + FILE_TYPES = ["ifcfg", "keys", "route", "route6", "rule", "rule6"] @classmethod def _file_types(cls, file_type): if file_type is None: return cls.FILE_TYPES else: - return [ file_type ] + return [file_type] @classmethod - def ifcfg_paths(cls, name, file_types = None): + def ifcfg_paths(cls, name, file_types=None): paths = [] if file_types is None: file_types = cls.FILE_TYPES @@ -1196,59 +1517,58 @@ class IfcfgUtil: return paths @classmethod - def ifcfg_path(cls, name, file_type = None): + def ifcfg_path(cls, name, file_type=None): n = str(name) - if not name or \ - n == '.' or \ - n == '..' or \ - n.find('/') != -1: - raise MyError('invalid ifcfg-name %s' % (name)) + if not name or n == "." or n == ".." or n.find("/") != -1: + raise MyError("invalid ifcfg-name %s" % (name)) if file_type is None: - file_type = 'ifcfg' + file_type = "ifcfg" if file_type not in cls.FILE_TYPES: - raise MyError('invalid file-type %s' % (file_type)) - return '/etc/sysconfig/network-scripts/' + file_type + '-' + n + raise MyError("invalid file-type %s" % (file_type)) + return "/etc/sysconfig/network-scripts/" + file_type + "-" + n @classmethod def KeyValid(cls, name): - r = getattr(cls, '_CHECKSTR_VALID_KEY', None) + r = getattr(cls, "_CHECKSTR_VALID_KEY", None) if r is None: import re - r = re.compile('^[a-zA-Z][a-zA-Z0-9_]*$') + + r = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$") cls._CHECKSTR_VALID_KEY = r return bool(r.match(name)) @classmethod def ValueEscape(cls, value): - r = getattr(cls, '_re_ValueEscape', None) + r = getattr(cls, "_re_ValueEscape", None) if r is None: import re - r = re.compile('^[a-zA-Z_0-9-.]*$') + + r = re.compile("^[a-zA-Z_0-9-.]*$") cls._re_ValueEscape = r if r.match(value): return value - if any([ord(c) < ord(' ') for c in value]): + if any([ord(c) < ord(" ") for c in value]): # needs ansic escaping due to ANSI control caracters (newline) - s = '$\'' + s = "$'" for c in value: if ord(c) < ord(c): - s += '\\' + str(ord(c)) - elif c == '\\' or c == '\'': - s += '\\' + c + s += "\\" + str(ord(c)) + elif c == "\\" or c == "'": + s += "\\" + c else: # non-unicode chars are fine too to take literally # as utf8 s += c - s += '\'' + s += "'" else: # double quoting s = '"' for c in value: - if c == '"' or c == '\\' or c == '$' or c == '`': - s += '\\' + c + if c == '"' or c == "\\" or c == "$" or c == "`": + s += "\\" + c else: # non-unicode chars are fine too to take literally # as utf8 @@ -1261,29 +1581,32 @@ class IfcfgUtil: if not append_only or current is None: if not route: return None - return '\n'.join(route) + '\n' + return "\n".join(route) + "\n" if route: - # the 'route' file is processed line by line by initscripts' ifup-route. Hence, - # the order of the route matters. _ifcfg_route_merge() is not sophisticated - # enough to understand pre-existing lines. It will only append lines that - # don't exist yet, which hopefully is correct. - # It's better to always rewrite the entire file with route_append_only=False. + # the 'route' file is processed line by line by initscripts' + # ifup-route. Hence, the order of the route matters. + # _ifcfg_route_merge() is not sophisticated enough to understand + # pre-existing lines. It will only append lines that don't exist + # yet, which hopefully is correct. It's better to always rewrite + # the entire file with route_append_only=False. changed = False - c_lines = list(current.split('\n')) + c_lines = list(current.split("\n")) for r in route: if r not in c_lines: changed = True c_lines.append(r) if changed: - return '\n'.join(c_lines) + '\n' + return "\n".join(c_lines) + "\n" return current @classmethod - def ifcfg_create(cls, connections, idx, warn_fcn = lambda msg: None, content_current = None): + def ifcfg_create( + cls, connections, idx, warn_fcn=lambda msg: None, content_current=None + ): connection = connections[idx] - ip = connection['ip'] + ip = connection["ip"] ifcfg = {} keys_file = None @@ -1292,173 +1615,190 @@ class IfcfgUtil: rule4_file = None rule6_file = None - if ip['dhcp4_send_hostname'] is not None: - warn_fcn('ip.dhcp4_send_hostname is not supported by initscripts provider') - if ip['route_metric4'] is not None and ip['route_metric4'] >= 0: - warn_fcn('ip.route_metric4 is not supported by initscripts provider') - if ip['route_metric6'] is not None and ip['route_metric6'] >= 0: - warn_fcn('ip.route_metric6 is not supported by initscripts provider') + if ip["dhcp4_send_hostname"] is not None: + warn_fcn("ip.dhcp4_send_hostname is not supported by initscripts provider") + if ip["route_metric4"] is not None and ip["route_metric4"] >= 0: + warn_fcn("ip.route_metric4 is not supported by initscripts provider") + if ip["route_metric6"] is not None and ip["route_metric6"] >= 0: + warn_fcn("ip.route_metric6 is not supported by initscripts provider") - ifcfg['NM_CONTROLLED'] = 'no' + ifcfg["NM_CONTROLLED"] = "no" - if connection['autoconnect']: - ifcfg['ONBOOT'] = 'yes' + if connection["autoconnect"]: + ifcfg["ONBOOT"] = "yes" else: - ifcfg['ONBOOT'] = 'no' + ifcfg["ONBOOT"] = "no" - ifcfg['DEVICE'] = connection['interface_name'] + ifcfg["DEVICE"] = connection["interface_name"] - if connection['type'] == 'ethernet': - ifcfg['TYPE'] = 'Ethernet' - ifcfg['HWADDR'] = connection['mac'] - elif connection['type'] == 'infiniband': - ifcfg['TYPE'] = 'InfiniBand' - ifcfg['HWADDR'] = connection['mac'] - ifcfg['CONNECTED_MODE'] = 'yes' if (connection['infiniband']['transport_mode'] == 'connected') else 'no' - if connection['infiniband']['p_key'] != -1: - ifcfg['PKEY'] = 'yes' - ifcfg['PKEY_ID'] = str(connection['infiniband']['p_key']) - if connection['parent']: - ifcfg['PHYSDEV'] = ArgUtil.connection_find_master(connection['parent'], connections, idx) - elif connection['type'] == 'bridge': - ifcfg['TYPE'] = 'Bridge' - elif connection['type'] == 'bond': - ifcfg['TYPE'] = 'Bond' - ifcfg['BONDING_MASTER'] = 'yes' - opts = [ 'mode=%s' % (connection['bond']['mode']) ] - if connection['bond']['miimon'] is not None: - opts.append(' miimon=%s' % (connection['bond']['miimon'])) - ifcfg['BONDING_OPTS'] = ' '.join(opts) - elif connection['type'] == 'team': - ifcfg['DEVICETYPE'] = 'Team' - elif connection['type'] == 'vlan': - ifcfg['VLAN'] = 'yes' - ifcfg['TYPE'] = 'Vlan' - ifcfg['PHYSDEV'] = ArgUtil.connection_find_master(connection['parent'], connections, idx) - ifcfg['VID'] = str(connection['vlan']['id']) + if connection["type"] == "ethernet": + ifcfg["TYPE"] = "Ethernet" + ifcfg["HWADDR"] = connection["mac"] + elif connection["type"] == "infiniband": + ifcfg["TYPE"] = "InfiniBand" + ifcfg["HWADDR"] = connection["mac"] + ifcfg["CONNECTED_MODE"] = ( + "yes" + if (connection["infiniband"]["transport_mode"] == "connected") + else "no" + ) + if connection["infiniband"]["p_key"] != -1: + ifcfg["PKEY"] = "yes" + ifcfg["PKEY_ID"] = str(connection["infiniband"]["p_key"]) + if connection["parent"]: + ifcfg["PHYSDEV"] = ArgUtil.connection_find_master( + connection["parent"], connections, idx + ) + elif connection["type"] == "bridge": + ifcfg["TYPE"] = "Bridge" + elif connection["type"] == "bond": + ifcfg["TYPE"] = "Bond" + ifcfg["BONDING_MASTER"] = "yes" + opts = ["mode=%s" % (connection["bond"]["mode"])] + if connection["bond"]["miimon"] is not None: + opts.append(" miimon=%s" % (connection["bond"]["miimon"])) + ifcfg["BONDING_OPTS"] = " ".join(opts) + elif connection["type"] == "team": + ifcfg["DEVICETYPE"] = "Team" + elif connection["type"] == "vlan": + ifcfg["VLAN"] = "yes" + ifcfg["TYPE"] = "Vlan" + ifcfg["PHYSDEV"] = ArgUtil.connection_find_master( + connection["parent"], connections, idx + ) + ifcfg["VID"] = str(connection["vlan"]["id"]) else: - raise MyError('unsupported type %s' % (connection['type'])) + raise MyError("unsupported type %s" % (connection["type"])) - if connection['mtu']: - ifcfg['MTU'] = str(connection['mtu']) + if connection["mtu"]: + ifcfg["MTU"] = str(connection["mtu"]) - if 'ethernet' in connection: - if connection['ethernet']['autoneg'] is not None: - if connection['ethernet']['autoneg']: - s = 'autoneg on' + if "ethernet" in connection: + if connection["ethernet"]["autoneg"] is not None: + if connection["ethernet"]["autoneg"]: + s = "autoneg on" else: - s = 'autoneg off speed %s duplex %s' % (connection['ethernet']['speed'], - connection['ethernet']['duplex']) - ifcfg['ETHTOOL_OPTS'] = s + s = "autoneg off speed %s duplex %s" % ( + connection["ethernet"]["speed"], + connection["ethernet"]["duplex"], + ) + ifcfg["ETHTOOL_OPTS"] = s - if connection['master'] is not None: - m = ArgUtil.connection_find_master(connection['master'], connections, idx) - if connection['slave_type'] == 'bridge': - ifcfg['BRIDGE'] = m - elif connection['slave_type'] == 'bond': - ifcfg['MASTER'] = m - ifcfg['SLAVE'] = 'yes' - elif connection['slave_type'] == 'team': - ifcfg['TEAM_MASTER'] = m - if 'TYPE' in ifcfg: - del ifcfg['TYPE'] - if connection['type'] != 'team': - ifcfg['DEVICETYPE'] = 'TeamPort' + if connection["master"] is not None: + m = ArgUtil.connection_find_master(connection["master"], connections, idx) + if connection["slave_type"] == "bridge": + ifcfg["BRIDGE"] = m + elif connection["slave_type"] == "bond": + ifcfg["MASTER"] = m + ifcfg["SLAVE"] = "yes" + elif connection["slave_type"] == "team": + ifcfg["TEAM_MASTER"] = m + if "TYPE" in ifcfg: + del ifcfg["TYPE"] + if connection["type"] != "team": + ifcfg["DEVICETYPE"] = "TeamPort" else: - raise MyError('invalid slave_type "%s"' % (connection['slave_type'])) + raise MyError("invalid slave_type '%s'" % (connection["slave_type"])) - if ip['route_append_only'] and content_current: - route4_file = content_current['route'] - route6_file = content_current['route6'] + if ip["route_append_only"] and content_current: + route4_file = content_current["route"] + route6_file = content_current["route6"] else: - if connection['zone']: - ifcfg['ZONE'] = connection['zone'] + if connection["zone"]: + ifcfg["ZONE"] = connection["zone"] - addrs4 = list([a for a in ip['address'] if a['family'] == socket.AF_INET]) - addrs6 = list([a for a in ip['address'] if a['family'] == socket.AF_INET6]) + addrs4 = list([a for a in ip["address"] if a["family"] == socket.AF_INET]) + addrs6 = list([a for a in ip["address"] if a["family"] == socket.AF_INET6]) - if ip['dhcp4']: - ifcfg['BOOTPROTO'] = 'dhcp' + if ip["dhcp4"]: + ifcfg["BOOTPROTO"] = "dhcp" elif addrs4: - ifcfg['BOOTPROTO'] = 'static' + ifcfg["BOOTPROTO"] = "static" else: - ifcfg['BOOTPROTO'] = 'none' + ifcfg["BOOTPROTO"] = "none" for i in range(0, len(addrs4)): - a = addrs4[i] - ifcfg['IPADDR' + ('' if i == 0 else str(i))] = a['address'] - ifcfg['PREFIX' + ('' if i == 0 else str(i))] = str(a['prefix']) - if ip['gateway4'] is not None: - ifcfg['GATEWAY'] = ip['gateway4'] + addr = addrs4[i] + ifcfg["IPADDR" + ("" if i == 0 else str(i))] = addr["address"] + ifcfg["PREFIX" + ("" if i == 0 else str(i))] = str(addr["prefix"]) + if ip["gateway4"] is not None: + ifcfg["GATEWAY"] = ip["gateway4"] - for idx, dns in enumerate(ip['dns']): - ifcfg['DNS' + str(idx+1)] = dns['address'] - if ip['dns_search']: - ifcfg['DOMAIN'] = ' '.join(ip['dns_search']) + for idx, dns in enumerate(ip["dns"]): + ifcfg["DNS" + str(idx + 1)] = dns["address"] + if ip["dns_search"]: + ifcfg["DOMAIN"] = " ".join(ip["dns_search"]) - if ip['auto6']: - ifcfg['IPV6INIT'] = 'yes' - ifcfg['IPV6_AUTOCONF'] = 'yes' + if ip["auto6"]: + ifcfg["IPV6INIT"] = "yes" + ifcfg["IPV6_AUTOCONF"] = "yes" elif addrs6: - ifcfg['IPV6INIT'] = 'yes' - ifcfg['IPV6_AUTOCONF'] = 'no' + ifcfg["IPV6INIT"] = "yes" + ifcfg["IPV6_AUTOCONF"] = "no" else: - ifcfg['IPV6INIT'] = 'no' + ifcfg["IPV6INIT"] = "no" if addrs6: - ifcfg['IPVADDR'] = addrs6[0]['address'] + '/' + str(addrs6[0]['prefix']) + ifcfg["IPVADDR"] = addrs6[0]["address"] + "/" + str(addrs6[0]["prefix"]) if len(addrs6) > 1: - ifcfg['IPVADDR_SECONDARIES'] = ' '.join([a['address'] + '/' + str(a['prefix']) for a in addrs6[1:]]) - if ip['gateway6'] is not None: - ifcfg['IPV6_DEFAULTGW'] = ip['gateway6'] + ifcfg["IPVADDR_SECONDARIES"] = " ".join( + [a["address"] + "/" + str(a["prefix"]) for a in addrs6[1:]] + ) + if ip["gateway6"] is not None: + ifcfg["IPV6_DEFAULTGW"] = ip["gateway6"] route4 = [] route6 = [] - for r in ip['route']: - line = r['network'] + '/' + str(r['prefix']) - if r['gateway']: - line += ' via ' + r['gateway'] - if r['metric'] != -1: - line += ' metric ' + str(r['metric']) + for r in ip["route"]: + line = r["network"] + "/" + str(r["prefix"]) + if r["gateway"]: + line += " via " + r["gateway"] + if r["metric"] != -1: + line += " metric " + str(r["metric"]) - if r['family'] == socket.AF_INET: + if r["family"] == socket.AF_INET: route4.append(line) else: route6.append(line) - route4_file = cls._ifcfg_route_merge(route4, - ip['route_append_only'] and content_current, - content_current['route'] if content_current else None) - route6_file = cls._ifcfg_route_merge(route6, - ip['route_append_only'] and content_current, - content_current['route6'] if content_current else None) + route4_file = cls._ifcfg_route_merge( + route4, + ip["route_append_only"] and content_current, + content_current["route"] if content_current else None, + ) + route6_file = cls._ifcfg_route_merge( + route6, + ip["route_append_only"] and content_current, + content_current["route6"] if content_current else None, + ) - if ip['rule_append_only'] and content_current: - rule4_file = content_current['rule'] - rule6_file = content_current['rule6'] + if ip["rule_append_only"] and content_current: + rule4_file = content_current["rule"] + rule6_file = content_current["rule6"] for key in list(ifcfg.keys()): v = ifcfg[key] if v is None: del ifcfg[key] continue - if type(v) == type(True): - ifcfg[key] = 'yes' if v else 'no' + if isinstance(v, bool): + ifcfg[key] = "yes" if v else "no" return { - 'ifcfg': ifcfg, - 'keys': keys_file, - 'route': route4_file, - 'route6': route6_file, - 'rule': rule4_file, - 'rule6': rule6_file, + "ifcfg": ifcfg, + "keys": keys_file, + "route": route4_file, + "route6": route6_file, + "rule": rule4_file, + "rule6": rule6_file, } @classmethod def ifcfg_parse_line(cls, line): - r1 = getattr(cls, '_re_parse_line1', None) + r1 = getattr(cls, "_re_parse_line1", None) if r1 is None: import re import shlex - r1 = re.compile('^[ \t]*([a-zA-Z_][a-zA-Z_0-9]*)=(.*)$') + + r1 = re.compile("^[ \t]*([a-zA-Z_][a-zA-Z_0-9]*)=(.*)$") cls._re_parse_line1 = r1 cls._shlex = shlex m = r1.match(line) @@ -1473,7 +1813,7 @@ class IfcfgUtil: # good enough for now. try: c = list(cls._shlex.split(val, comments=True, posix=True)) - except: + except Exception: return None if len(c) != 1: return None @@ -1491,21 +1831,21 @@ class IfcfgUtil: return ifcfg @classmethod - def content_from_dict(cls, ifcfg_all, file_type = None, header = None): + def content_from_dict(cls, ifcfg_all, file_type=None, header=None): content = {} for file_type in cls._file_types(file_type): h = ifcfg_all[file_type] - if file_type == 'ifcfg': + if file_type == "ifcfg": if header is not None: - s = header + '\n' + s = header + "\n" else: s = "" for key in sorted(h.keys()): value = h[key] if not cls.KeyValid(key): - raise MyError('invalid ifcfg key %s' % (key)) + raise MyError("invalid ifcfg key %s" % (key)) if value is not None: - s += key + '=' + cls.ValueEscape(value) + '\n' + s += key + "=" + cls.ValueEscape(value) + "\n" content[file_type] = s else: content[file_type] = h @@ -1513,27 +1853,27 @@ class IfcfgUtil: return content @classmethod - def content_to_dict(cls, content, file_type = None): + def content_to_dict(cls, content, file_type=None): ifcfg_all = {} for file_type in cls._file_types(file_type): ifcfg_all[file_type] = cls.ifcfg_parse(content[file_type]) return ifcfg_all @classmethod - def content_from_file(cls, name, file_type = None): + def content_from_file(cls, name, file_type=None): content = {} for file_type in cls._file_types(file_type): path = cls.ifcfg_path(name, file_type) try: - with open(path, 'r') as content_file: + with open(path, "r") as content_file: i_content = content_file.read() - except Exception as e: + except Exception: i_content = None content[file_type] = i_content return content @classmethod - def content_to_file(cls, name, content, file_type = None): + def content_to_file(cls, name, content, file_type=None): for file_type in cls._file_types(file_type): path = cls.ifcfg_path(name, file_type) h = content[file_type] @@ -1542,10 +1882,11 @@ class IfcfgUtil: os.unlink(path) except OSError as e: import errno + if e.errno != errno.ENOENT: raise else: - with open(path, 'w') as text_file: + with open(path, "w") as text_file: text_file.write(h) @classmethod @@ -1558,30 +1899,31 @@ class IfcfgUtil: # # But first we need to find the interface name. Do # some naive parsing and check for DEVICE setting. - content = cls.content_from_file(name, 'ifcfg') - if content['ifcfg'] is not None: - content = cls.ifcfg_parse(content['ifcfg']) + content = cls.content_from_file(name, "ifcfg") + if content["ifcfg"] is not None: + content = cls.ifcfg_parse(content["ifcfg"]) else: content = {} - if 'DEVICE' not in content: + if "DEVICE" not in content: return None - path = '/sys/class/net/' + content['DEVICE'] + '/operstate' + path = "/sys/class/net/" + content["DEVICE"] + "/operstate" try: - with open(path, 'r') as content_file: + with open(path, "r") as content_file: i_content = str(content_file.read()) - except Exception as e: + except Exception: return None - if i_content.strip() != 'up': + if i_content.strip() != "up": return False return True + ############################################################################### -class NMUtil: - def __init__(self, nmclient = None): +class NMUtil: + def __init__(self, nmclient=None): if nmclient is None: nmclient = Util.NM().Client.new(None) self.nmclient = nmclient @@ -1589,7 +1931,7 @@ class NMUtil: def setting_ip_config_get_routes(self, s_ip): if s_ip is not None: for i in range(0, s_ip.get_num_routes()): - yield s_ip.get_route(i) + yield s_ip.get_route(i) def connection_ensure_setting(self, connection, setting_type): setting = connection.get_setting(setting_type) @@ -1602,22 +1944,33 @@ class NMUtil: if dev: NM = Util.NM() GObject = Util.GObject() - if GObject.type_is_a(dev, NM.DeviceBond) \ - or GObject.type_is_a(dev, NM.DeviceBridge) \ - or GObject.type_is_a(dev, NM.DeviceTeam): + if ( + GObject.type_is_a(dev, NM.DeviceBond) + or GObject.type_is_a(dev, NM.DeviceBridge) + or GObject.type_is_a(dev, NM.DeviceTeam) + ): return True return False - def active_connection_list(self, connections = None, black_list = None): + def active_connection_list(self, connections=None, black_list=None): active_cons = self.nmclient.get_active_connections() if connections: connections = set(connections) - active_cons = [ac for ac in active_cons if ac.get_connection() in connections] + active_cons = [ + ac for ac in active_cons if ac.get_connection() in connections + ] if black_list: active_cons = [ac for ac in active_cons if ac not in black_list] return list(active_cons) - def connection_list(self, name = None, uuid = None, black_list = None, black_list_names = None, black_list_uuids = None): + def connection_list( + self, + name=None, + uuid=None, + black_list=None, + black_list_names=None, + black_list_uuids=None, + ): cons = self.nmclient.get_connections() if name is not None: cons = [c for c in cons if c.get_id() == name] @@ -1632,6 +1985,7 @@ class NMUtil: cons = [c for c in cons if c.get_id() not in black_list_names] cons = list(cons) + def _cmp(a, b): s_a = a.get_setting_connection() s_b = b.get_setting_connection() @@ -1658,35 +2012,39 @@ class NMUtil: cons.sort(cmp=_cmp) return cons - def connection_compare(self, con_a, con_b, normalize_a = False, normalize_b = False, compare_flags = None): + def connection_compare( + self, con_a, con_b, normalize_a=False, normalize_b=False, compare_flags=None + ): NM = Util.NM() if normalize_a: con_a = NM.SimpleConnection.new_clone(con_a) try: con_a.normalize() - except: + except Exception: pass if normalize_b: con_b = NM.SimpleConnection.new_clone(con_b) try: con_b.normalize() - except: + except Exception: pass - if compare_flags == None: + if compare_flags is None: compare_flags = NM.SettingCompareFlags.IGNORE_TIMESTAMP - return not(not(con_a.compare (con_b, compare_flags))) + return not (not (con_a.compare(con_b, compare_flags))) def connection_is_active(self, con): NM = Util.NM() for ac in self.active_connection_list(connections=[con]): - if ac.get_state() >= NM.ActiveConnectionState.ACTIVATING \ - and ac.get_state() <= NM.ActiveConnectionState.ACTIVATED: + if ( + ac.get_state() >= NM.ActiveConnectionState.ACTIVATING + and ac.get_state() <= NM.ActiveConnectionState.ACTIVATED + ): return True return False - def connection_create(self, connections, idx, connection_current = None): + def connection_create(self, connections, idx, connection_current=None): NM = Util.NM() connection = connections[idx] @@ -1694,131 +2052,185 @@ class NMUtil: con = NM.SimpleConnection.new() s_con = self.connection_ensure_setting(con, NM.SettingConnection) - s_con.set_property(NM.SETTING_CONNECTION_ID, connection['name']) - s_con.set_property(NM.SETTING_CONNECTION_UUID, connection['nm.uuid']) - s_con.set_property(NM.SETTING_CONNECTION_AUTOCONNECT, connection['autoconnect']) - s_con.set_property(NM.SETTING_CONNECTION_INTERFACE_NAME, connection['interface_name']) + s_con.set_property(NM.SETTING_CONNECTION_ID, connection["name"]) + s_con.set_property(NM.SETTING_CONNECTION_UUID, connection["nm.uuid"]) + s_con.set_property(NM.SETTING_CONNECTION_AUTOCONNECT, connection["autoconnect"]) + s_con.set_property( + NM.SETTING_CONNECTION_INTERFACE_NAME, connection["interface_name"] + ) - if connection['type'] == 'ethernet': - s_con.set_property(NM.SETTING_CONNECTION_TYPE, '802-3-ethernet') + if connection["type"] == "ethernet": + s_con.set_property(NM.SETTING_CONNECTION_TYPE, "802-3-ethernet") s_wired = self.connection_ensure_setting(con, NM.SettingWired) - s_wired.set_property(NM.SETTING_WIRED_MAC_ADDRESS, connection['mac']) - elif connection['type'] == 'infiniband': - s_con.set_property(NM.SETTING_CONNECTION_TYPE, 'infiniband') + s_wired.set_property(NM.SETTING_WIRED_MAC_ADDRESS, connection["mac"]) + elif connection["type"] == "infiniband": + s_con.set_property(NM.SETTING_CONNECTION_TYPE, "infiniband") s_infiniband = self.connection_ensure_setting(con, NM.SettingInfiniband) - s_infiniband.set_property(NM.SETTING_INFINIBAND_MAC_ADDRESS, connection['mac']) - s_infiniband.set_property(NM.SETTING_INFINIBAND_TRANSPORT_MODE, connection['infiniband']['transport_mode']) - if connection['infiniband']['p_key'] != -1: - s_infiniband.set_property(NM.SETTING_INFINIBAND_P_KEY, connection['infiniband']['p_key']) - if connection['parent']: - s_infiniband.set_property(NM.SETTING_INFINIBAND_PARENT, ArgUtil.connection_find_master(connection['parent'], connections, idx)) - elif connection['type'] == 'bridge': - s_con.set_property(NM.SETTING_CONNECTION_TYPE, 'bridge') + s_infiniband.set_property( + NM.SETTING_INFINIBAND_MAC_ADDRESS, connection["mac"] + ) + s_infiniband.set_property( + NM.SETTING_INFINIBAND_TRANSPORT_MODE, + connection["infiniband"]["transport_mode"], + ) + if connection["infiniband"]["p_key"] != -1: + s_infiniband.set_property( + NM.SETTING_INFINIBAND_P_KEY, connection["infiniband"]["p_key"] + ) + if connection["parent"]: + s_infiniband.set_property( + NM.SETTING_INFINIBAND_PARENT, + ArgUtil.connection_find_master( + connection["parent"], connections, idx + ), + ) + elif connection["type"] == "bridge": + s_con.set_property(NM.SETTING_CONNECTION_TYPE, "bridge") s_bridge = self.connection_ensure_setting(con, NM.SettingBridge) s_bridge.set_property(NM.SETTING_BRIDGE_STP, False) - elif connection['type'] == 'bond': - s_con.set_property(NM.SETTING_CONNECTION_TYPE, 'bond') + elif connection["type"] == "bond": + s_con.set_property(NM.SETTING_CONNECTION_TYPE, "bond") s_bond = self.connection_ensure_setting(con, NM.SettingBond) - s_bond.add_option('mode', connection['bond']['mode']) - if connection['bond']['miimon'] is not None: - s_bond.add_option('miimon', str(connection['bond']['miimon'])) - elif connection['type'] == 'team': - s_con.set_property(NM.SETTING_CONNECTION_TYPE, 'team') - elif connection['type'] == 'vlan': + s_bond.add_option("mode", connection["bond"]["mode"]) + if connection["bond"]["miimon"] is not None: + s_bond.add_option("miimon", str(connection["bond"]["miimon"])) + elif connection["type"] == "team": + s_con.set_property(NM.SETTING_CONNECTION_TYPE, "team") + elif connection["type"] == "vlan": s_vlan = self.connection_ensure_setting(con, NM.SettingVlan) - s_vlan.set_property(NM.SETTING_VLAN_ID, connection['vlan']['id']) - s_vlan.set_property(NM.SETTING_VLAN_PARENT, ArgUtil.connection_find_master_uuid(connection['parent'], connections, idx)) - elif connection['type'] == 'macvlan': + s_vlan.set_property(NM.SETTING_VLAN_ID, connection["vlan"]["id"]) + s_vlan.set_property( + NM.SETTING_VLAN_PARENT, + ArgUtil.connection_find_master_uuid( + connection["parent"], connections, idx + ), + ) + elif connection["type"] == "macvlan": # convert mode name to a number (which is actually expected by nm) - mode = connection['macvlan']['mode'] + mode = connection["macvlan"]["mode"] try: - mode_id = int(getattr( NM.SettingMacvlanMode, mode.upper() )) - except AttributeError as e: - raise MyError('Macvlan mode "%s" is not recognized' % (mode)) + mode_id = int(getattr(NM.SettingMacvlanMode, mode.upper())) + except AttributeError: + raise MyError("Macvlan mode '%s' is not recognized" % (mode)) s_macvlan = self.connection_ensure_setting(con, NM.SettingMacvlan) s_macvlan.set_property(NM.SETTING_MACVLAN_MODE, mode_id) - s_macvlan.set_property(NM.SETTING_MACVLAN_PROMISCUOUS, connection['macvlan']['promiscuous']) - s_macvlan.set_property(NM.SETTING_MACVLAN_TAP, connection['macvlan']['tap']) - s_macvlan.set_property(NM.SETTING_MACVLAN_PARENT, ArgUtil.connection_find_master(connection['parent'], connections, idx)) + s_macvlan.set_property( + NM.SETTING_MACVLAN_PROMISCUOUS, connection["macvlan"]["promiscuous"] + ) + s_macvlan.set_property(NM.SETTING_MACVLAN_TAP, connection["macvlan"]["tap"]) + s_macvlan.set_property( + NM.SETTING_MACVLAN_PARENT, + ArgUtil.connection_find_master(connection["parent"], connections, idx), + ) else: - raise MyError('unsupported type %s' % (connection['type'])) + raise MyError("unsupported type %s" % (connection["type"])) - if 'ethernet' in connection: - if connection['ethernet']['autoneg'] is not None: + if "ethernet" in connection: + if connection["ethernet"]["autoneg"] is not None: s_wired = self.connection_ensure_setting(con, NM.SettingWired) - s_wired.set_property(NM.SETTING_WIRED_AUTO_NEGOTIATE, connection['ethernet']['autoneg']) - s_wired.set_property(NM.SETTING_WIRED_DUPLEX, connection['ethernet']['duplex']) - s_wired.set_property(NM.SETTING_WIRED_SPEED, connection['ethernet']['speed']) + s_wired.set_property( + NM.SETTING_WIRED_AUTO_NEGOTIATE, connection["ethernet"]["autoneg"] + ) + s_wired.set_property( + NM.SETTING_WIRED_DUPLEX, connection["ethernet"]["duplex"] + ) + s_wired.set_property( + NM.SETTING_WIRED_SPEED, connection["ethernet"]["speed"] + ) - if connection['mtu']: - if connection['type'] == 'infiniband': + if connection["mtu"]: + if connection["type"] == "infiniband": s_infiniband = self.connection_ensure_setting(con, NM.SettingInfiniband) - s_infiniband.set_property(NM.SETTING_INFINIBAND_MTU, connection['mtu']) + s_infiniband.set_property(NM.SETTING_INFINIBAND_MTU, connection["mtu"]) else: s_wired = self.connection_ensure_setting(con, NM.SettingWired) - s_wired.set_property(NM.SETTING_WIRED_MTU, connection['mtu']) + s_wired.set_property(NM.SETTING_WIRED_MTU, connection["mtu"]) - if connection['master'] is not None: - s_con.set_property(NM.SETTING_CONNECTION_SLAVE_TYPE, connection['slave_type']) - s_con.set_property(NM.SETTING_CONNECTION_MASTER, ArgUtil.connection_find_master_uuid(connection['master'], connections, idx)) + if connection["master"] is not None: + s_con.set_property( + NM.SETTING_CONNECTION_SLAVE_TYPE, connection["slave_type"] + ) + s_con.set_property( + NM.SETTING_CONNECTION_MASTER, + ArgUtil.connection_find_master_uuid( + connection["master"], connections, idx + ), + ) else: - if connection['zone']: - s_con.set_property(NM.SETTING_CONNECTION_ZONE, connection['zone']) + if connection["zone"]: + s_con.set_property(NM.SETTING_CONNECTION_ZONE, connection["zone"]) - ip = connection['ip'] + ip = connection["ip"] s_ip4 = self.connection_ensure_setting(con, NM.SettingIP4Config) s_ip6 = self.connection_ensure_setting(con, NM.SettingIP6Config) - s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, 'auto') - s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, 'auto') + s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") + s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") - addrs4 = list([a for a in ip['address'] if a['family'] == socket.AF_INET]) - addrs6 = list([a for a in ip['address'] if a['family'] == socket.AF_INET6]) + addrs4 = list([a for a in ip["address"] if a["family"] == socket.AF_INET]) + addrs6 = list([a for a in ip["address"] if a["family"] == socket.AF_INET6]) - if ip['dhcp4']: - s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, 'auto') - s_ip4.set_property(NM.SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, ip['dhcp4_send_hostname'] != False) + if ip["dhcp4"]: + s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") + s_ip4.set_property( + NM.SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, + ip["dhcp4_send_hostname"] is not False, + ) elif addrs4: - s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, 'manual') + s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "manual") else: - s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, 'disabled') + s_ip4.set_property(NM.SETTING_IP_CONFIG_METHOD, "disabled") for a in addrs4: - s_ip4.add_address(NM.IPAddress.new(a['family'], a['address'], a['prefix'])) - if ip['gateway4'] is not None: - s_ip4.set_property(NM.SETTING_IP_CONFIG_GATEWAY, ip['gateway4']) - if ip['route_metric4'] is not None and ip['route_metric4'] >= 0: - s_ip4.set_property(NM.SETTING_IP_CONFIG_ROUTE_METRIC, ip['route_metric4']) - for d in ip['dns']: - if d['family'] == socket.AF_INET: - s_ip4.add_dns(d['address']) - for s in ip['dns_search']: + s_ip4.add_address( + NM.IPAddress.new(a["family"], a["address"], a["prefix"]) + ) + if ip["gateway4"] is not None: + s_ip4.set_property(NM.SETTING_IP_CONFIG_GATEWAY, ip["gateway4"]) + if ip["route_metric4"] is not None and ip["route_metric4"] >= 0: + s_ip4.set_property( + NM.SETTING_IP_CONFIG_ROUTE_METRIC, ip["route_metric4"] + ) + for d in ip["dns"]: + if d["family"] == socket.AF_INET: + s_ip4.add_dns(d["address"]) + for s in ip["dns_search"]: s_ip4.add_dns_search(s) - if ip['auto6']: - s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, 'auto') + if ip["auto6"]: + s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "auto") elif addrs6: - s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, 'manual') + s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "manual") else: - s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, 'ignore') + s_ip6.set_property(NM.SETTING_IP_CONFIG_METHOD, "ignore") for a in addrs6: - s_ip6.add_address(NM.IPAddress.new(a['family'], a['address'], a['prefix'])) - if ip['gateway6'] is not None: - s_ip6.set_property(NM.SETTING_IP_CONFIG_GATEWAY, ip['gateway6']) - if ip['route_metric6'] is not None and ip['route_metric6'] >= 0: - s_ip6.set_property(NM.SETTING_IP_CONFIG_ROUTE_METRIC, ip['route_metric6']) - for d in ip['dns']: - if d['family'] == socket.AF_INET6: - s_ip6.add_dns(d['address']) + s_ip6.add_address( + NM.IPAddress.new(a["family"], a["address"], a["prefix"]) + ) + if ip["gateway6"] is not None: + s_ip6.set_property(NM.SETTING_IP_CONFIG_GATEWAY, ip["gateway6"]) + if ip["route_metric6"] is not None and ip["route_metric6"] >= 0: + s_ip6.set_property( + NM.SETTING_IP_CONFIG_ROUTE_METRIC, ip["route_metric6"] + ) + for d in ip["dns"]: + if d["family"] == socket.AF_INET6: + s_ip6.add_dns(d["address"]) - if ip['route_append_only'] and connection_current: - for r in self.setting_ip_config_get_routes(connection_current.get_setting(NM.SettingIP4Config)): + if ip["route_append_only"] and connection_current: + for r in self.setting_ip_config_get_routes( + connection_current.get_setting(NM.SettingIP4Config) + ): s_ip4.add_route(r) - for r in self.setting_ip_config_get_routes(connection_current.get_setting(NM.SettingIP6Config)): + for r in self.setting_ip_config_get_routes( + connection_current.get_setting(NM.SettingIP6Config) + ): s_ip6.add_route(r) - for r in ip['route']: - rr = NM.IPRoute.new(r['family'], r['network'], r['prefix'], r['gateway'], r['metric']) - if r['family'] == socket.AF_INET: + for r in ip["route"]: + rr = NM.IPRoute.new( + r["family"], r["network"], r["prefix"], r["gateway"], r["metric"] + ) + if r["family"] == socket.AF_INET: s_ip4.add_route(rr) else: s_ip6.add_route(rr) @@ -1826,11 +2238,10 @@ class NMUtil: try: con.normalize() except Exception as e: - raise MyError('created connection failed to normalize: %s' % (e)) + raise MyError("created connection failed to normalize: %s" % (e)) return con - def connection_add(self, con, timeout = 10): - + def connection_add(self, con, timeout=10): def add_cb(client, result, cb_args): con = None try: @@ -1838,8 +2249,8 @@ class NMUtil: except Exception as e: if Util.error_is_cancelled(e): return - cb_args['error'] = str(e) - cb_args['con'] = con + cb_args["error"] = str(e) + cb_args["con"] = con Util.GMainLoop().quit() cancellable = Util.create_cancellable() @@ -1847,12 +2258,15 @@ class NMUtil: self.nmclient.add_connection_async(con, True, cancellable, add_cb, cb_args) if not Util.GMainLoop_run(timeout): cancellable.cancel() - raise MyError('failure to add connection: %s' % ('timeout')) - if not cb_args.get('con', None): - raise MyError('failure to add connection: %s' % (cb_args.get('error', 'unknown error'))) - return cb_args['con'] + raise MyError("failure to add connection: %s" % ("timeout")) + if not cb_args.get("con", None): + raise MyError( + "failure to add connection: %s" + % (cb_args.get("error", "unknown error")) + ) + return cb_args["con"] - def connection_update(self, con, con_new, timeout = 10): + def connection_update(self, con, con_new, timeout=10): con.replace_settings_from_connection(con_new) def update_cb(connection, result, cb_args): @@ -1862,8 +2276,8 @@ class NMUtil: except Exception as e: if Util.error_is_cancelled(e): return - cb_args['error'] = str(e) - cb_args['success'] = success + cb_args["error"] = str(e) + cb_args["success"] = success Util.GMainLoop().quit() cancellable = Util.create_cancellable() @@ -1871,12 +2285,15 @@ class NMUtil: con.commit_changes_async(True, cancellable, update_cb, cb_args) if not Util.GMainLoop_run(timeout): cancellable.cancel() - raise MyError('failure to update connection: %s' % ('timeout')) - if not cb_args.get('success', False): - raise MyError('failure to update connection: %s' % (cb_args.get('error', 'unknown error'))) + raise MyError("failure to update connection: %s" % ("timeout")) + if not cb_args.get("success", False): + raise MyError( + "failure to update connection: %s" + % (cb_args.get("error", "unknown error")) + ) return True - def connection_delete(self, connection, timeout = 10): + def connection_delete(self, connection, timeout=10): c_uuid = connection.get_uuid() @@ -1887,8 +2304,8 @@ class NMUtil: except Exception as e: if Util.error_is_cancelled(e): return - cb_args['error'] = str(e) - cb_args['success'] = success + cb_args["error"] = str(e) + cb_args["success"] = success Util.GMainLoop().quit() cancellable = Util.create_cancellable() @@ -1896,29 +2313,36 @@ class NMUtil: connection.delete_async(cancellable, delete_cb, cb_args) if not Util.GMainLoop_run(timeout): cancellable.cancel() - raise MyError('failure to delete connection: %s' % ('timeout')) - if not cb_args.get('success', False): - raise MyError('failure to delete connection: %s' % (cb_args.get('error', 'unknown error'))) + raise MyError("failure to delete connection: %s" % ("timeout")) + if not cb_args.get("success", False): + raise MyError( + "failure to delete connection: %s" + % (cb_args.get("error", "unknown error")) + ) # workaround libnm oddity. The connection may not yet be gone if the # connection was active and is deactivating. Wait. wait_count = 0 while True: - connections = self.connection_list(uuid = c_uuid) + connections = self.connection_list(uuid=c_uuid) if not connections: return wait_count += 1 if wait_count > 10: - break; + break import time + time.sleep(1) Util.GMainLoop_iterate_all() - raise MyError('connection %s was supposedly deleted successfully, but it\'s still here' % (c_uuid)) + raise MyError( + "connection %s was supposedly deleted successfully, but it's still here" + % (c_uuid) + ) - def connection_activate(self, connection, timeout = 15, wait_time = None): + def connection_activate(self, connection, timeout=15, wait_time=None): - already_retried = False; + already_retried = False while True: @@ -1929,34 +2353,41 @@ class NMUtil: except Exception as e: if Util.error_is_cancelled(e): return - cb_args['error'] = str(e) - cb_args['active_connection'] = active_connection + cb_args["error"] = str(e) + cb_args["active_connection"] = active_connection Util.GMainLoop().quit() cancellable = Util.create_cancellable() cb_args = {} - self.nmclient.activate_connection_async(connection, None, None, cancellable, activate_cb, cb_args) + self.nmclient.activate_connection_async( + connection, None, None, cancellable, activate_cb, cb_args + ) if not Util.GMainLoop_run(timeout): cancellable.cancel() - raise MyError('failure to activate connection: %s' % ('timeout')) + raise MyError("failure to activate connection: %s" % ("timeout")) - if cb_args.get('active_connection', None): - ac = cb_args['active_connection'] + if cb_args.get("active_connection", None): + ac = cb_args["active_connection"] self.connection_activate_wait(ac, wait_time) return ac - # there is a bug in NetworkManager, that the connection might already be in the process - # of activating. In that case, NM would reject the activation request with - # "Connection '$PROFILE' is not available on the device $DEV at this time." + # there is a bug in NetworkManager, that the connection + # might already be in the process of activating. In that + # case, NM would reject the activation request with + # "Connection '$PROFILE' is not available on the device $DEV + # at this time." # # Try to work around it by waiting a bit and retrying. if already_retried: - raise MyError('failure to activate connection: %s' % (cb_args.get('error', 'unknown error'))) + raise MyError( + "failure to activate connection: %s" + % (cb_args.get("error", "unknown error")) + ) already_retried = True import time - time.sleep(1) + time.sleep(1) def connection_activate_wait(self, ac, wait_time): @@ -1969,41 +2400,65 @@ class NMUtil: if state == NM.ActiveConnectionState.ACTIVATED: return if state != NM.ActiveConnectionState.ACTIVATING: - raise MyError('activation is in unexpected state "%s"' % (state)) + raise MyError("activation is in unexpected state '%s'" % (state)) def check_activated(ac, dev): ac_state = ac.get_state() - # the state reason was for active-connection was introduced in NM 1.8 API. - # Work around for older library version. + # the state reason was for active-connection was introduced + # in NM 1.8 API. Work around for older library version. try: ac_reason = ac.get_state_reason() - except AttributeError as e: + except AttributeError: ac_reason = None if dev: dev_state = dev.get_state() if ac_state == NM.ActiveConnectionState.ACTIVATING: - if self.device_is_master_type(dev) \ - and dev_state >= NM.DeviceState.IP_CONFIG \ - and dev_state <= NM.DeviceState.ACTIVATED: - # master connections qualify as activated once they reach IP-Config state. - # That is because they may wait for slave devices to attach + if ( + self.device_is_master_type(dev) + and dev_state >= NM.DeviceState.IP_CONFIG + and dev_state <= NM.DeviceState.ACTIVATED + ): + # master connections qualify as activated once they + # reach IP-Config state. That is because they may + # wait for slave devices to attach return True, None # fall through elif ac_state == NM.ActiveConnectionState.ACTIVATED: return True, None elif ac_state == NM.ActiveConnectionState.DEACTIVATED: - if not dev \ - or ( ac_reason is not None \ - and ac_reason != NM.ActiveConnectionStateReason.DEVICE_DISCONNECTED) \ - or dev.get_active_connection() is not ac: - return True, ((ac_reason.value_nick if ac_reason else None) or 'unknown reason') + if ( + not dev + or ( + ac_reason is not None + and ac_reason + != NM.ActiveConnectionStateReason.DEVICE_DISCONNECTED + ) + or dev.get_active_connection() is not ac + ): + return ( + True, + ( + (ac_reason.value_nick if ac_reason else None) + or "unknown reason" + ), + ) # the state of the active connection is not very helpful. # see if the device-state is better. - if dev_state <= NM.DeviceState.DISCONNECTED or dev_state > NM.DeviceState.DEACTIVATING: - return True, (dev.get_state_reason().value_nick or (ac_reason.value_nick if ac_reason else None) or 'unknown reason') + if ( + dev_state <= NM.DeviceState.DISCONNECTED + or dev_state > NM.DeviceState.DEACTIVATING + ): + return ( + True, + ( + dev.get_state_reason().value_nick + or (ac_reason.value_nick if ac_reason else None) + or "unknown reason" + ), + ) # fall through, wait longer for a better state reason. # wait longer. @@ -2016,6 +2471,7 @@ class NMUtil: if not complete: cb_out = [] + def check_activated_cb(): complete, failure_reason = check_activated(ac, dev) if complete: @@ -2023,16 +2479,21 @@ class NMUtil: Util.GMainLoop().quit() try: - # 'state-changed' signal is 1.8 API. Workaround for older libnm API version - ac_id = ac.connect('state-changed', lambda source, state, reason: check_activated_cb()) - except: + # 'state-changed' signal is 1.8 API. Workaround for + # older libnm API version + ac_id = ac.connect( + "state-changed", lambda source, state, reason: check_activated_cb() + ) + except Exception: ac_id = None if dev: - dev_id = dev.connect('notify::state', lambda source, pspec: check_activated_cb()) + dev_id = dev.connect( + "notify::state", lambda source, pspec: check_activated_cb() + ) try: if not Util.GMainLoop_run(wait_time): - raise MyError('connection not fully activated after timeout') + raise MyError("connection not fully activated after timeout") finally: if dev: dev.handler_disconnect(dev_id) @@ -2042,10 +2503,9 @@ class NMUtil: failure_reason = cb_out[0] if failure_reason: - raise MyError('connection not activated: %s' % (failure_reason)) - - def active_connection_deactivate(self, ac, timeout = 10, wait_time = None): + raise MyError("connection not activated: %s" % (failure_reason)) + def active_connection_deactivate(self, ac, timeout=10, wait_time=None): def deactivate_cb(client, result, cb_args): success = False try: @@ -2053,18 +2513,23 @@ class NMUtil: except Exception as e: if Util.error_is_cancelled(e): return - cb_args['error'] = str(e) - cb_args['success'] = success + cb_args["error"] = str(e) + cb_args["success"] = success Util.GMainLoop().quit() cancellable = Util.create_cancellable() cb_args = {} - self.nmclient.deactivate_connection_async(ac, cancellable, deactivate_cb, cb_args) + self.nmclient.deactivate_connection_async( + ac, cancellable, deactivate_cb, cb_args + ) if not Util.GMainLoop_run(timeout): cancellable.cancel() - raise MyError('failure to deactivate connection: %s' % (timeout)) - if not cb_args.get('success', False): - raise MyError('failure to deactivate connection: %s' % (cb_args.get('error', 'unknown error'))) + raise MyError("failure to deactivate connection: %s" % (timeout)) + if not cb_args.get("success", False): + raise MyError( + "failure to deactivate connection: %s" + % (cb_args.get("error", "unknown error")) + ) self.active_connection_deactivate_wait(ac, wait_time) return True @@ -2085,19 +2550,21 @@ class NMUtil: if check_deactivated(ac): Util.GMainLoop().quit() - ac_id = ac.connect('notify::state', lambda source, pspec: check_deactivated_cb()) + ac_id = ac.connect( + "notify::state", lambda source, pspec: check_deactivated_cb() + ) try: if not Util.GMainLoop_run(wait_time): - raise MyError('connection not fully deactivated after timeout') + raise MyError("connection not fully deactivated after timeout") finally: ac.handler_disconnect(ac_id) ############################################################################### -class RunEnvironment: +class RunEnvironment: def __init__(self): self._check_mode = None @@ -2105,41 +2572,48 @@ class RunEnvironment: def ifcfg_header(self): return None - def log(self, - connections, - idx, - severity, - msg, - is_changed = False, - ignore_errors = False, - warn_traceback = False, - force_fail = False): + def log( + self, + connections, + idx, + severity, + msg, + is_changed=False, + ignore_errors=False, + warn_traceback=False, + force_fail=False, + ): raise NotImplementedError() - def run_command(self, argv, encoding = None): + def run_command(self, argv, encoding=None): raise NotImplementedError() def _check_mode_changed(self, old_check_mode, new_check_mode, connections): raise NotImplementedError() - def check_mode_set(self, check_mode, connections = None): + def check_mode_set(self, check_mode, connections=None): c = self._check_mode self._check_mode = check_mode - assert( (c is None and check_mode in [CheckMode.PREPARE]) \ - or (c == CheckMode.PREPARE and check_mode in [CheckMode.PRE_RUN, CheckMode.DRY_RUN]) \ - or (c == CheckMode.PRE_RUN and check_mode in [CheckMode.REAL_RUN]) \ - or (c == CheckMode.REAL_RUN and check_mode in [CheckMode.DONE]) \ - or (c == CheckMode.DRY_RUN and check_mode in [CheckMode.DONE])) + assert ( + (c is None and check_mode in [CheckMode.PREPARE]) + or ( + c == CheckMode.PREPARE + and check_mode in [CheckMode.PRE_RUN, CheckMode.DRY_RUN] + ) + or (c == CheckMode.PRE_RUN and check_mode in [CheckMode.REAL_RUN]) + or (c == CheckMode.REAL_RUN and check_mode in [CheckMode.DONE]) + or (c == CheckMode.DRY_RUN and check_mode in [CheckMode.DONE]) + ) self._check_mode_changed(c, check_mode, connections) class RunEnvironmentAnsible(RunEnvironment): ARGS = { - 'ignore_errors': { 'required': False, 'default': False, 'type': 'str' }, - 'force_state_change': { 'required': False, 'default': False, 'type': 'bool' }, - 'provider': { 'required': True, 'default': None, 'type': 'str' }, - 'connections': { 'required': False, 'default': None, 'type': 'list' }, + "ignore_errors": {"required": False, "default": False, "type": "str"}, + "force_state_change": {"required": False, "default": False, "type": "bool"}, + "provider": {"required": True, "default": None, "type": "str"}, + "connections": {"required": False, "default": None, "type": "list"}, } def __init__(self): @@ -2148,25 +2622,21 @@ class RunEnvironmentAnsible(RunEnvironment): self._log_idx = 0 from ansible.module_utils.basic import AnsibleModule - module = AnsibleModule( - argument_spec = self.ARGS, - supports_check_mode = True, - ) + + module = AnsibleModule(argument_spec=self.ARGS, supports_check_mode=True) self.module = module @property def ifcfg_header(self): - return '# this file was created by ansible' + return "# this file was created by ansible" - def run_command(self, argv, encoding = None): - return self.module.run_command(argv, encoding = encoding) + def run_command(self, argv, encoding=None): + return self.module.run_command(argv, encoding=encoding) def _run_results_push(self, n_connections): c = [] for cc in range(0, n_connections + 1): - c.append({ - 'log': [], - }) + c.append({"log": []}) self._run_results.append(c) @property @@ -2185,75 +2655,89 @@ class RunEnvironmentAnsible(RunEnvironment): del self._run_results[-1] self._run_results_push(n_connections) - def log(self, - connections, - idx, - severity, - msg, - is_changed = False, - ignore_errors = False, - warn_traceback = False, - force_fail = False): - assert(idx >= -1) + def log( + self, + connections, + idx, + severity, + msg, + is_changed=False, + ignore_errors=False, + warn_traceback=False, + force_fail=False, + ): + assert idx >= -1 self._log_idx += 1 - self.run_results[idx]['log'].append((severity, msg, self._log_idx)) + self.run_results[idx]["log"].append((severity, msg, self._log_idx)) if severity == LogLevel.ERROR: - if force_fail \ - or not ignore_errors: - self.fail_json(connections, 'error: %s' % (msg), changed = is_changed, warn_traceback = warn_traceback) + if force_fail or not ignore_errors: + self.fail_json( + connections, + "error: %s" % (msg), + changed=is_changed, + warn_traceback=warn_traceback, + ) def _complete_kwargs_loglines(self, rr, connections, idx): if idx == len(connections): - prefix = '#' + prefix = "#" else: c = connections[idx] - prefix = '#%s, state:%s' % (idx, c['state']) - if c['state'] != 'wait': - prefix = prefix + (', "%s"' % (c['name'])) - for r in rr['log']: - yield (r[2], '[%03d] %s %s: %s' % (r[2], LogLevel.fmt(r[0]), prefix, r[1])) + prefix = "#%s, state:%s" % (idx, c["state"]) + if c["state"] != "wait": + prefix = prefix + (", '%s'" % (c["name"])) + for r in rr["log"]: + yield (r[2], "[%03d] %s %s: %s" % (r[2], LogLevel.fmt(r[0]), prefix, r[1])) - def _complete_kwargs(self, connections, kwargs, traceback_msg = None): - if 'warnings' in kwargs: - logs = list(kwargs['warnings']) + def _complete_kwargs(self, connections, kwargs, traceback_msg=None): + if "warnings" in kwargs: + logs = list(kwargs["warnings"]) else: logs = [] - l = [] + loglines = [] for res in self._run_results: for idx, rr in enumerate(res): - l.extend(self._complete_kwargs_loglines(rr, connections, idx)) - l.sort(key = lambda x: x[0]) - logs.extend([x[1] for x in l]) + loglines.extend(self._complete_kwargs_loglines(rr, connections, idx)) + loglines.sort(key=lambda x: x[0]) + logs.extend([x[1] for x in loglines]) if traceback_msg is not None: logs.append(traceback_msg) - kwargs['warnings'] = logs + kwargs["warnings"] = logs return kwargs - def exit_json(self, connections, changed = False, **kwargs): - kwargs['changed'] = changed + def exit_json(self, connections, changed=False, **kwargs): + kwargs["changed"] = changed self.module.exit_json(**self._complete_kwargs(connections, kwargs)) - def fail_json(self, connections, msg, changed = False, warn_traceback = False, **kwargs): + def fail_json( + self, connections, msg, changed=False, warn_traceback=False, **kwargs + ): traceback_msg = None if warn_traceback: - traceback_msg = 'exception: %s' % (traceback.format_exc()) - kwargs['msg'] = msg - kwargs['changed'] = changed - self.module.fail_json(**self._complete_kwargs(connections, kwargs, traceback_msg)) + traceback_msg = "exception: %s" % (traceback.format_exc()) + kwargs["msg"] = msg + kwargs["changed"] = changed + self.module.fail_json( + **self._complete_kwargs(connections, kwargs, traceback_msg) + ) + ############################################################################### -class Cmd: - def __init__(self, - run_env, - connections_unvalidated, - connection_validator, - is_check_mode = False, - ignore_errors = False, - force_state_change = False): +class Cmd: + def __init__( + self, + run_env, + connections_unvalidated, + connection_validator, + is_check_mode=False, + ignore_errors=False, + force_state_change=False, + ): self.run_env = run_env + self.validate_one_type = None self._connections_unvalidated = connections_unvalidated self._connection_validator = connection_validator self._is_check_mode = is_check_mode @@ -2265,8 +2749,8 @@ class Cmd: self._check_mode = CheckMode.PREPARE self._is_changed_modified_system = False - def run_command(argv, encoding = None): - return self.run_env.run_command(argv, encoding = encoding) + def run_command(self, argv, encoding=None): + return self.run_env.run_command(argv, encoding=encoding) @property def is_changed_modified_system(self): @@ -2279,7 +2763,7 @@ class Cmd: try: c = self._connection_validator.validate(self._connections_unvalidated) except ValidationError as e: - raise MyError('configuration error: %s' % (e)) + raise MyError("configuration error: %s" % (e)) self._connections = c return c @@ -2287,26 +2771,31 @@ class Cmd: def connections_data(self): c = self._connections_data if c is None: - assert(self.check_mode in [CheckMode.DRY_RUN, CheckMode.PRE_RUN, CheckMode.REAL_RUN]) + assert self.check_mode in [ + CheckMode.DRY_RUN, + CheckMode.PRE_RUN, + CheckMode.REAL_RUN, + ] c = [] - for idx in range(0, len(self.connections)): - c.append({ - 'changed': False, - }) + for _ in range(0, len(self.connections)): + c.append({"changed": False}) self._connections_data = c return c def connections_data_reset(self): for c in self.connections_data: - c['changed'] = False + c["changed"] = False - def connections_data_set_changed(self, idx, changed = True): - assert(self._check_mode in [CheckMode.PRE_RUN, CheckMode.DRY_RUN, CheckMode.REAL_RUN]) + def connections_data_set_changed(self, idx, changed=True): + assert self._check_mode in [ + CheckMode.PRE_RUN, + CheckMode.DRY_RUN, + CheckMode.REAL_RUN, + ] if not changed: return - self.connections_data[idx]['changed'] = changed - if changed \ - and self._check_mode in [CheckMode.DRY_RUN, CheckMode.REAL_RUN]: + self.connections_data[idx]["changed"] = changed + if changed and self._check_mode in [CheckMode.DRY_RUN, CheckMode.REAL_RUN]: # we only do actual modifications during the REAL_RUN step. # And as a special exception, during the DRY_RUN step, which # is like REAL_RUN, except not not actually changing anything. @@ -2321,39 +2810,48 @@ class Cmd: def log_warn(self, idx, msg): self.log(idx, LogLevel.WARN, msg) - def log_error(self, idx, msg, warn_traceback = False, force_fail = False): - self.log(idx, LogLevel.ERROR, msg, warn_traceback = warn_traceback, force_fail = force_fail) + def log_error(self, idx, msg, warn_traceback=False, force_fail=False): + self.log( + idx, + LogLevel.ERROR, + msg, + warn_traceback=warn_traceback, + force_fail=force_fail, + ) - def log_fatal(self, idx, msg, warn_traceback = False): - self.log(idx, LogLevel.ERROR, msg, warn_traceback = warn_traceback, force_fail = True) + def log_fatal(self, idx, msg, warn_traceback=False): + self.log( + idx, LogLevel.ERROR, msg, warn_traceback=warn_traceback, force_fail=True + ) - def log(self, idx, severity, msg, warn_traceback = False, force_fail = False): - self.run_env.log(self.connections, - idx, - severity, - msg, - is_changed = self.is_changed_modified_system, - ignore_errors = self.connection_ignore_errors( - self.connections[idx]), - warn_traceback = warn_traceback, - force_fail = force_fail) + def log(self, idx, severity, msg, warn_traceback=False, force_fail=False): + self.run_env.log( + self.connections, + idx, + severity, + msg, + is_changed=self.is_changed_modified_system, + ignore_errors=self.connection_ignore_errors(self.connections[idx]), + warn_traceback=warn_traceback, + force_fail=force_fail, + ) @staticmethod def create(provider, **kwargs): - if provider == 'nm': + if provider == "nm": return Cmd_nm(**kwargs) - elif provider == 'initscripts': + elif provider == "initscripts": return Cmd_initscripts(**kwargs) - raise MyError('unsupported provider %s' % (provider)) + raise MyError("unsupported provider %s" % (provider)) def connection_force_state_change(self, connection): - v = connection['force_state_change'] + v = connection["force_state_change"] if v is not None: return v return self._force_state_change def connection_ignore_errors(self, connection): - v = connection['ignore_errors'] + v = connection["ignore_errors"] if v is not None: return v return self._ignore_errors @@ -2363,31 +2861,33 @@ class Cmd: # modify the connection. con = self.connections[idx] - assert(con['state'] in ['up', 'down']) + assert con["state"] in ["up", "down"] # also check, if the current profile is 'up' with a 'type' (which # possibly modifies the connection as well) - if con['state'] == 'up' \ - and 'type' in con \ - and self.connections_data[idx]['changed']: + if ( + con["state"] == "up" + and "type" in con + and self.connections_data[idx]["changed"] + ): return True for i in reversed(range(idx)): c = self.connections[i] - if 'name' not in c: + if "name" not in c: continue - if c['name'] != con['name']: + if c["name"] != con["name"]: continue - c_state = c['state'] - if c_state == 'up' and 'type' not in c: + c_state = c["state"] + if c_state == "up" and "type" not in c: pass - elif c_state == 'down': + elif c_state == "down": return True - elif c_state == 'absent': + elif c_state == "absent": return True - elif c_state in ['present', 'up']: - if self.connections_data[idx]['changed']: + elif c_state in ["present", "up"]: + if self.connections_data[idx]["changed"]: return True return False @@ -2417,70 +2917,108 @@ class Cmd: self.run_env.check_mode_set(CheckMode.PREPARE, self.connections) for idx, connection in enumerate(self.connections): try: - self._connection_validator.validate_connection_one(self.validate_one_type, - self.connections, - idx) + self._connection_validator.validate_connection_one( + self.validate_one_type, self.connections, idx + ) except ValidationError as e: self.log_fatal(idx, str(e)) self.run_prepare() while self.check_mode_next() != CheckMode.DONE: for idx, connection in enumerate(self.connections): try: - state = connection['state'] - if state == 'wait': - w = connection['wait'] + state = connection["state"] + if state == "wait": + w = connection["wait"] if w is None: w = 10 - self.log_info(idx, 'wait for %s seconds' % (w)) + self.log_info(idx, "wait for %s seconds" % (w)) if self.check_mode == CheckMode.REAL_RUN: import time + time.sleep(w) - elif state == 'absent': + elif state == "absent": self.run_state_absent(idx) - elif state == 'present': + elif state == "present": self.run_state_present(idx) - elif state == 'up': - if 'type' in connection: + elif state == "up": + if "type" in connection: self.run_state_present(idx) self.run_state_up(idx) - elif state == 'down': + elif state == "down": self.run_state_down(idx) else: assert False except Exception as e: - self.log_warn(idx, 'failure: %s [[%s]]' % (e, traceback.format_exc())) + self.log_warn( + idx, "failure: %s [[%s]]" % (e, traceback.format_exc()) + ) raise def run_prepare(self): for idx, connection in enumerate(self.connections): - if 'type' in connection and connection['check_iface_exists']: - # when the profile is tied to a certain interface via 'interface_name' or 'mac', - # check that such an interface exists. + if "type" in connection and connection["check_iface_exists"]: + # when the profile is tied to a certain interface via + # 'interface_name' or 'mac', check that such an interface + # exists. # - # This check has many flaws, as we don't check whether the existing - # interface has the right device type. Also, there is some ambiguity - # between the current MAC address and the permanent MAC address. + # This check has many flaws, as we don't check whether the + # existing interface has the right device type. Also, there is + # some ambiguity between the current MAC address and the + # permanent MAC address. li_mac = None li_ifname = None - if connection['mac']: - li_mac = SysUtil.link_info_find(mac = connection['mac']) + if connection["mac"]: + li_mac = SysUtil.link_info_find(mac=connection["mac"]) if not li_mac: - self.log_fatal(idx, 'profile specifies mac "%s" but no such interface exists' % (connection['mac'])) - if connection['interface_name']: - li_ifname = SysUtil.link_info_find(ifname = connection['interface_name']) + self.log_fatal( + idx, + "profile specifies mac '%s' but no such interface exists" + % (connection["mac"]), + ) + if connection["interface_name"]: + li_ifname = SysUtil.link_info_find( + ifname=connection["interface_name"] + ) if not li_ifname: - if connection['type'] == 'ethernet': - self.log_fatal(idx, 'profile specifies interface_name "%s" but no such interface exists' % (connection['interface_name'])) - elif connection['type'] == 'infiniband': - if connection['infiniband']['p_key'] != -1: - self.log_fatal(idx, 'profile specifies interface_name "%s" but no such infiniband interface exists' % (connection['interface_name'])) + if connection["type"] == "ethernet": + self.log_fatal( + idx, + "profile specifies interface_name '%s' but no such " + "interface exists" % (connection["interface_name"]), + ) + elif connection["type"] == "infiniband": + if connection["infiniband"]["p_key"] != -1: + self.log_fatal( + idx, + "profile specifies interface_name '%s' but no such " + "infiniband interface exists" + % (connection["interface_name"]), + ) if li_mac and li_ifname and li_mac != li_ifname: - self.log_fatal(idx, 'profile specifies interface_name "%s" and mac "%s" but no such interface exists' % (connection['interface_name'], connection['mac'])) + self.log_fatal( + idx, + "profile specifies interface_name '%s' and mac '%s' but no " + "such interface exists" + % (connection["interface_name"], connection["mac"]), + ) + + def run_state_absent(self, idx): + raise NotImplementedError() + + def run_state_present(self, idx): + raise NotImplementedError() + + def run_state_down(self, idx): + raise NotImplementedError() + + def run_state_up(self, idx): + raise NotImplementedError() + ############################################################################### -class Cmd_nm(Cmd): +class Cmd_nm(Cmd): def __init__(self, **kwargs): Cmd.__init__(self, **kwargs) self._nmutil = None @@ -2492,7 +3030,7 @@ class Cmd_nm(Cmd): try: nmclient = Util.NM().Client.new(None) except Exception as e: - raise MyError('failure loading libnm library: %s' % (e)) + raise MyError("failure loading libnm library: %s" % (e)) self._nmutil = NMUtil(nmclient) return self._nmutil @@ -2500,104 +3038,133 @@ class Cmd_nm(Cmd): Cmd.run_prepare(self) names = {} for connection in self.connections: - if connection['state'] not in ['up', 'down', 'present', 'absent']: + if connection["state"] not in ["up", "down", "present", "absent"]: continue - name = connection['name'] + name = connection["name"] if not name: - assert(connection['state'] == 'absent') - continue; + assert connection["state"] == "absent" + continue if name in names: - exists = names[name]['nm.exists'] - uuid = names[name]['nm.uuid'] + exists = names[name]["nm.exists"] + uuid = names[name]["nm.uuid"] else: - c = Util.first(self.nmutil.connection_list(name = name)) + c = Util.first(self.nmutil.connection_list(name=name)) - exists = (c is not None) + exists = c is not None if c is not None: uuid = c.get_uuid() else: uuid = Util.create_uuid() - names[name] = { - 'nm.exists': exists, - 'nm.uuid': uuid, - } - connection['nm.exists'] = exists - connection['nm.uuid'] = uuid + names[name] = {"nm.exists": exists, "nm.uuid": uuid} + connection["nm.exists"] = exists + connection["nm.uuid"] = uuid def run_state_absent(self, idx): seen = set() - name = self.connections[idx]['name'] + name = self.connections[idx]["name"] black_list_names = None if not name: name = None black_list_names = ArgUtil.connection_get_non_absent_names(self.connections) while True: - connections = self.nmutil.connection_list(name = name, black_list_names = black_list_names, black_list = seen) + connections = self.nmutil.connection_list( + name=name, black_list_names=black_list_names, black_list=seen + ) if not connections: break c = connections[-1] seen.add(c) - self.log_info(idx, 'delete connection %s, %s' % (c.get_id(), c.get_uuid())) + self.log_info(idx, "delete connection %s, %s" % (c.get_id(), c.get_uuid())) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: self.nmutil.connection_delete(c) except MyError as e: - self.log_error(idx, 'delete connection failed: %s' % (e)) + self.log_error(idx, "delete connection failed: %s" % (e)) if not seen: - self.log_info(idx, 'no connection "%s"' % (name)) + self.log_info(idx, "no connection '%s'" % (name)) def run_state_present(self, idx): connection = self.connections[idx] - con_cur = Util.first(self.nmutil.connection_list(name = connection['name'], uuid = connection['nm.uuid'])) + con_cur = Util.first( + self.nmutil.connection_list( + name=connection["name"], uuid=connection["nm.uuid"] + ) + ) con_new = self.nmutil.connection_create(self.connections, idx, con_cur) if con_cur is None: - self.log_info(idx, 'add connection %s, %s' % (connection['name'], connection['nm.uuid'])) + self.log_info( + idx, + "add connection %s, %s" % (connection["name"], connection["nm.uuid"]), + ) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: con_cur = self.nmutil.connection_add(con_new) except MyError as e: - self.log_error(idx, 'adding connection failed: %s' % (e)) - elif not self.nmutil.connection_compare(con_cur, con_new, normalize_a = True): - self.log_info(idx, 'update connection %s, %s' % (con_cur.get_id(), con_cur.get_uuid())) + self.log_error(idx, "adding connection failed: %s" % (e)) + elif not self.nmutil.connection_compare(con_cur, con_new, normalize_a=True): + self.log_info( + idx, "update connection %s, %s" % (con_cur.get_id(), con_cur.get_uuid()) + ) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: self.nmutil.connection_update(con_cur, con_new) except MyError as e: - self.log_error(idx, 'updating connection failed: %s' % (e)) + self.log_error(idx, "updating connection failed: %s" % (e)) else: - self.log_info(idx, 'connection %s, %s already up to date' % (con_cur.get_id(), con_cur.get_uuid())) + self.log_info( + idx, + "connection %s, %s already up to date" + % (con_cur.get_id(), con_cur.get_uuid()), + ) seen = set() if con_cur is not None: seen.add(con_cur) while True: - connections = self.nmutil.connection_list(name = connection['name'], black_list = seen, black_list_uuids = [connection['nm.uuid']]) + connections = self.nmutil.connection_list( + name=connection["name"], + black_list=seen, + black_list_uuids=[connection["nm.uuid"]], + ) if not connections: break c = connections[-1] - self.log_info(idx, 'delete duplicate connection %s, %s' % (c.get_id(), c.get_uuid())) + self.log_info( + idx, "delete duplicate connection %s, %s" % (c.get_id(), c.get_uuid()) + ) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: - self.nmutil.connection_delete(c) + self.nmutil.connection_delete(c) except MyError as e: - self.log_error(idx, 'delete duplicate connection failed: %s' % (e)) + self.log_error(idx, "delete duplicate connection failed: %s" % (e)) seen.add(c) - def run_state_up(self, idx): connection = self.connections[idx] - con = Util.first(self.nmutil.connection_list(name = connection['name'], uuid = connection['nm.uuid'])) + con = Util.first( + self.nmutil.connection_list( + name=connection["name"], uuid=connection["nm.uuid"] + ) + ) if not con: if self.check_mode == CheckMode.REAL_RUN: - self.log_error(idx, 'up connection %s, %s failed: no connection' % (connection['name'], connection['nm.uuid'])) + self.log_error( + idx, + "up connection %s, %s failed: no connection" + % (connection["name"], connection["nm.uuid"]), + ) else: - self.log_info(idx, 'up connection %s, %s' % (connection['name'], connection['nm.uuid'])) + self.log_info( + idx, + "up connection %s, %s" + % (connection["name"], connection["nm.uuid"]), + ) return is_active = self.nmutil.connection_is_active(con) @@ -2605,105 +3172,132 @@ class Cmd_nm(Cmd): force_state_change = self.connection_force_state_change(connection) if is_active and not force_state_change and not is_modified: - self.log_info(idx, 'up connection %s, %s skipped because already active' % - (con.get_id(), con.get_uuid())) + self.log_info( + idx, + "up connection %s, %s skipped because already active" + % (con.get_id(), con.get_uuid()), + ) return - self.log_info(idx, 'up connection %s, %s (%s)' % - (con.get_id(), con.get_uuid(), - 'not-active' if not is_active else \ - 'is-modified' if is_modified else \ - 'force-state-change')) + self.log_info( + idx, + "up connection %s, %s (%s)" + % ( + con.get_id(), + con.get_uuid(), + "not-active" + if not is_active + else "is-modified" + if is_modified + else "force-state-change", + ), + ) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: - ac = self.nmutil.connection_activate (con) + ac = self.nmutil.connection_activate(con) except MyError as e: - self.log_error(idx, 'up connection failed: %s' % (e)) + self.log_error(idx, "up connection failed: %s" % (e)) - wait_time = connection['wait'] + wait_time = connection["wait"] if wait_time is None: wait_time = 90 try: self.nmutil.connection_activate_wait(ac, wait_time) except MyError as e: - self.log_error(idx, 'up connection failed while waiting: %s' % (e)) + self.log_error(idx, "up connection failed while waiting: %s" % (e)) def run_state_down(self, idx): connection = self.connections[idx] - cons = self.nmutil.connection_list(name = connection['name']) + cons = self.nmutil.connection_list(name=connection["name"]) changed = False if cons: seen = set() while True: - ac = Util.first(self.nmutil.active_connection_list(connections = cons, black_list = seen)) + ac = Util.first( + self.nmutil.active_connection_list( + connections=cons, black_list=seen + ) + ) if ac is None: break seen.add(ac) - self.log_info(idx, 'down connection %s: %s' % (connection['name'], ac.get_path())) + self.log_info( + idx, "down connection %s: %s" % (connection["name"], ac.get_path()) + ) changed = True self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: self.nmutil.active_connection_deactivate(ac) except MyError as e: - self.log_error(idx, 'down connection failed: %s' % (e)) + self.log_error(idx, "down connection failed: %s" % (e)) - wait_time = connection['wait'] + wait_time = connection["wait"] if wait_time is None: wait_time = 10 try: self.nmutil.active_connection_deactivate_wait(ac, wait_time) except MyError as e: - self.log_error(idx, 'down connection failed while waiting: %s' % (e)) + self.log_error( + idx, "down connection failed while waiting: %s" % (e) + ) - cons = self.nmutil.connection_list(name = connection['name']) + cons = self.nmutil.connection_list(name=connection["name"]) if not changed: - self.log_error(idx, - 'down connection %s failed: connection not found' % - (connection['name'])) + self.log_error( + idx, + "down connection %s failed: connection not found" + % (connection["name"]), + ) ############################################################################### -class Cmd_initscripts(Cmd): +class Cmd_initscripts(Cmd): def __init__(self, **kwargs): Cmd.__init__(self, **kwargs) - self.validate_one_type = ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS + self.validate_one_type = ( + ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS + ) def run_prepare(self): Cmd.run_prepare(self) for idx, connection in enumerate(self.connections): - if connection.get('type') in [ 'macvlan' ]: - self.log_fatal(idx, 'unsupported type %s for initscripts provider' % (connection['type'])) + if connection.get("type") in ["macvlan"]: + self.log_fatal( + idx, + "unsupported type %s for initscripts provider" + % (connection["type"]), + ) - def check_name(self, idx, name = None): + def check_name(self, idx, name=None): if name is None: - name = self.connections[idx]['name'] + name = self.connections[idx]["name"] try: f = IfcfgUtil.ifcfg_path(name) - except MyError as e: - self.log_error(idx, 'invalid name %s for connection' % (name)) + except MyError: + self.log_error(idx, "invalid name %s for connection" % (name)) return None return f def run_state_absent(self, idx): - n = self.connections[idx]['name'] + n = self.connections[idx]["name"] name = n if not name: names = [] black_list_names = ArgUtil.connection_get_non_absent_names(self.connections) - for f in os.listdir('/etc/sysconfig/network-scripts'): - if not f.startswith('ifcfg-'): + for f in os.listdir("/etc/sysconfig/network-scripts"): + if not f.startswith("ifcfg-"): continue name = f[6:] if name in black_list_names: continue - if name == 'lo': + if name == "lo": continue names.append(name) else: @@ -2717,56 +3311,65 @@ class Cmd_initscripts(Cmd): if not os.path.isfile(path): continue changed = True - self.log_info(idx, 'delete ifcfg-rh file "%s"' % (path)) + self.log_info(idx, "delete ifcfg-rh file '%s'" % (path)) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: os.unlink(path) except Exception as e: - self.log_error(idx, 'delete ifcfg-rh file "%s" failed: %s' % (path, e)) + self.log_error( + idx, "delete ifcfg-rh file '%s' failed: %s" % (path, e) + ) if not changed: - self.log_info(idx, 'delete ifcfg-rh files for %s (no files present)' % ('"'+n+'"' if n else '*')) + self.log_info( + idx, + "delete ifcfg-rh files for %s (no files present)" + % ("'" + n + "'" if n else "*"), + ) def run_state_present(self, idx): if not self.check_name(idx): return connection = self.connections[idx] - name = connection['name'] + name = connection["name"] old_content = IfcfgUtil.content_from_file(name) - ifcfg_all = IfcfgUtil.ifcfg_create(self.connections, idx, - lambda msg: self.log_warn(idx, msg), - old_content) + ifcfg_all = IfcfgUtil.ifcfg_create( + self.connections, idx, lambda msg: self.log_warn(idx, msg), old_content + ) - new_content = IfcfgUtil.content_from_dict(ifcfg_all, - header = self.run_env.ifcfg_header) + new_content = IfcfgUtil.content_from_dict( + ifcfg_all, header=self.run_env.ifcfg_header + ) if old_content == new_content: - self.log_info(idx, 'ifcfg-rh profile "%s" already up to date' % (name)) + self.log_info(idx, "ifcfg-rh profile '%s' already up to date" % (name)) return - op = 'add' if (old_content['ifcfg'] is None) else 'update' + op = "add" if (old_content["ifcfg"] is None) else "update" - self.log_info(idx, '%s ifcfg-rh profile "%s"' % (op, name)) + self.log_info(idx, "%s ifcfg-rh profile '%s'" % (op, name)) self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: try: IfcfgUtil.content_to_file(name, new_content) except MyError as e: - self.log_error(idx, '%s ifcfg-rh profile "%s" failed: %s' % (op, name, e)) + self.log_error( + idx, "%s ifcfg-rh profile '%s' failed: %s" % (op, name, e) + ) def _run_state_updown(self, idx, do_up): if not self.check_name(idx): return connection = self.connections[idx] - name = connection['name'] + name = connection["name"] - if connection['wait'] is not None: + if connection["wait"] is not None: # initscripts don't support wait, they always block until the ifup/ifdown # command completes. Silently ignore the argument. pass @@ -2774,9 +3377,11 @@ class Cmd_initscripts(Cmd): path = IfcfgUtil.ifcfg_path(name) if not os.path.isfile(path): if self.check_mode == CheckMode.REAL_RUN: - self.log_error(idx, 'ifcfg file "%s" does not exist' % (path)) + self.log_error(idx, "ifcfg file '%s' does not exist" % (path)) else: - self.log_info(idx, 'ifcfg file "%s" does not exist in check mode' % (path)) + self.log_info( + idx, "ifcfg file '%s' does not exist in check mode" % (path) + ) return is_active = IfcfgUtil.connection_seems_active(name) @@ -2785,35 +3390,49 @@ class Cmd_initscripts(Cmd): if do_up: if is_active is True and not force_state_change and not is_modified: - self.log_info(idx, 'up connection %s skipped because already active' % - (name)) + self.log_info( + idx, "up connection %s skipped because already active" % (name) + ) return - self.log_info(idx, 'up connection %s (%s)' % - (name, - 'not-active' if is_active is not True else \ - 'is-modified' if is_modified else \ - 'force-state-change')) - cmd = 'ifup' + self.log_info( + idx, + "up connection %s (%s)" + % ( + name, + "not-active" + if is_active is not True + else "is-modified" + if is_modified + else "force-state-change", + ), + ) + cmd = "ifup" else: if is_active is False and not force_state_change: - self.log_info(idx, 'down connection %s skipped because not active' % - (name)) + self.log_info( + idx, "down connection %s skipped because not active" % (name) + ) return - self.log_info(idx, 'up connection %s (%s)' % - (name, - 'active' if is_active is not False else \ - 'force-state-change')) - cmd = 'ifdown' + self.log_info( + idx, + "up connection %s (%s)" + % (name, "active" if is_active is not False else "force-state-change"), + ) + cmd = "ifdown" self.connections_data_set_changed(idx) if self.check_mode == CheckMode.REAL_RUN: rc, out, err = self.run_env.run_command([cmd, name]) - self.log_info(idx, 'call `%s %s`: rc=%d, out="%s", err="%s"' % (cmd, name, rc, out, err)) + self.log_info( + idx, + "call '%s %s': rc=%d, out='%s', err='%s'" % (cmd, name, rc, out, err), + ) if rc != 0: - self.log_error(idx, 'call `%s %s` failed with exit status %d' % (cmd, name, rc)) - + self.log_error( + idx, "call '%s %s' failed with exit status %d" % (cmd, name, rc) + ) def run_state_up(self, idx): self._run_state_updown(idx, True) @@ -2821,30 +3440,38 @@ class Cmd_initscripts(Cmd): def run_state_down(self, idx): self._run_state_updown(idx, False) + ############################################################################### + def main(): connections = None cmd = None run_env_ansible = RunEnvironmentAnsible() try: params = run_env_ansible.module.params - cmd = Cmd.create(params['provider'], - run_env = run_env_ansible, - connections_unvalidated = params['connections'], - connection_validator = ArgValidator_ListConnections(), - is_check_mode = run_env_ansible.module.check_mode, - ignore_errors = params['ignore_errors'], - force_state_change = params['force_state_change']) + cmd = Cmd.create( + params["provider"], + run_env=run_env_ansible, + connections_unvalidated=params["connections"], + connection_validator=ArgValidator_ListConnections(), + is_check_mode=run_env_ansible.module.check_mode, + ignore_errors=params["ignore_errors"], + force_state_change=params["force_state_change"], + ) connections = cmd.connections cmd.run() except Exception as e: - run_env_ansible.fail_json(connections, - 'fatal error: %s' % (e), - changed = (cmd is not None and cmd.is_changed_modified_system), - warn_traceback = not isinstance(e, MyError)) - run_env_ansible.exit_json(connections, - changed = (cmd is not None and cmd.is_changed_modified_system)) + run_env_ansible.fail_json( + connections, + "fatal error: %s" % (e), + changed=(cmd is not None and cmd.is_changed_modified_system), + warn_traceback=not isinstance(e, MyError), + ) + run_env_ansible.exit_json( + connections, changed=(cmd is not None and cmd.is_changed_modified_system) + ) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/tests/test_network_connections.py b/tests/test_network_connections.py index 7ac042d..e25a8f4 100755 --- a/tests/test_network_connections.py +++ b/tests/test_network_connections.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +""" Tests for network_connections Ansible module """ # SPDX-License-Identifier: BSD-3-Clause import sys @@ -11,7 +12,9 @@ TESTS_BASEDIR = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(1, os.path.join(TESTS_BASEDIR, "..", "library")) import network_connections as n -from network_connections import SysUtil, Util + +from network_connections import SysUtil +from network_connections import Util try: @@ -24,10 +27,11 @@ except AttributeError: else: return lambda x: x + try: nmutil = n.NMUtil() - assert(nmutil) -except: + assert nmutil +except Exception: # NMUtil is not supported, for example on RHEL 6 or without # pygobject. nmutil = None @@ -36,35 +40,45 @@ if nmutil: NM = n.Util.NM() GObject = n.Util.GObject() + def pprint(msg, obj): - print('PRINT: %s\n' % (msg)) + print("PRINT: %s\n" % (msg)) import pprint - p = pprint.PrettyPrinter(indent = 4) + + p = pprint.PrettyPrinter(indent=4) p.pprint(obj) if nmutil is not None and isinstance(obj, NM.Connection): obj.dump() + ARGS_CONNECTIONS = n.ArgValidator_ListConnections() -class TestValidator(unittest.TestCase): +class TestValidator(unittest.TestCase): def assertValidationError(self, v, value): - self.assertRaises(n.ValidationError, - v.validate, - value) + self.assertRaises(n.ValidationError, v.validate, value) def assert_nm_connection_routes_expected(self, connection, route_list_expected): - parser = n.ArgValidatorIPRoute('route[?]') + parser = n.ArgValidatorIPRoute("route[?]") route_list_exp = [parser.validate(r) for r in route_list_expected] - route_list_new = itertools.chain(nmutil.setting_ip_config_get_routes(connection.get_setting(NM.SettingIP4Config)), - nmutil.setting_ip_config_get_routes(connection.get_setting(NM.SettingIP6Config))) - route_list_new = [{ - 'family': r.get_family(), - 'network': r.get_dest(), - 'prefix': int(r.get_prefix()), - 'gateway': r.get_next_hop(), - 'metric': int(r.get_metric()), - } for r in route_list_new] + route_list_new = itertools.chain( + nmutil.setting_ip_config_get_routes( + connection.get_setting(NM.SettingIP4Config) + ), + nmutil.setting_ip_config_get_routes( + connection.get_setting(NM.SettingIP6Config) + ), + ) + route_list_new = [ + { + "family": r.get_family(), + "network": r.get_dest(), + "prefix": int(r.get_prefix()), + "gateway": r.get_next_hop(), + "metric": int(r.get_metric()), + } + for r in route_list_new + ] self.assertEqual(route_list_exp, route_list_new) def do_connections_check_invalid(self, input_connections): @@ -75,38 +89,47 @@ class TestValidator(unittest.TestCase): return connections = ARGS_CONNECTIONS.validate(input_connections) for connection in connections: - if 'type' in connection: - connection['nm.exists'] = False - connection['nm.uuid'] = n.Util.create_uuid() + if "type" in connection: + connection["nm.exists"] = False + connection["nm.uuid"] = n.Util.create_uuid() mode = n.ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS for idx, connection in enumerate(connections): try: ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx) - except n.ValidationError as e: + except n.ValidationError: continue - if 'type' in connection: + if "type" in connection: con_new = nmutil.connection_create(connections, idx) self.assertTrue(con_new) self.assertTrue(con_new.verify()) - if 'nm_route_list_current' in kwargs: - parser = n.ArgValidatorIPRoute('route[?]') + if "nm_route_list_current" in kwargs: + parser = n.ArgValidatorIPRoute("route[?]") s4 = con_new.get_setting(NM.SettingIP4Config) s6 = con_new.get_setting(NM.SettingIP6Config) s4.clear_routes() s6.clear_routes() - for r in kwargs['nm_route_list_current'][idx]: + for r in kwargs["nm_route_list_current"][idx]: r = parser.validate(r) - r = NM.IPRoute.new(r['family'], r['network'], r['prefix'], r['gateway'], r['metric']) + r = NM.IPRoute.new( + r["family"], + r["network"], + r["prefix"], + r["gateway"], + r["metric"], + ) if r.get_family() == socket.AF_INET: s4.add_route(r) else: s6.add_route(r) - con_new = nmutil.connection_create(connections, idx, - connection_current = con_new) + con_new = nmutil.connection_create( + connections, idx, connection_current=con_new + ) self.assertTrue(con_new) self.assertTrue(con_new.verify()) - if 'nm_route_list_expected' in kwargs: - self.assert_nm_connection_routes_expected(con_new, kwargs['nm_route_list_expected'][idx]) + if "nm_route_list_expected" in kwargs: + self.assert_nm_connection_routes_expected( + con_new, kwargs["nm_route_list_expected"][idx] + ) def do_connections_validate_ifcfg(self, input_connections, **kwargs): mode = n.ArgValidator_ListConnections.VALIDATE_ONE_MODE_INITSCRIPTS @@ -114,24 +137,27 @@ class TestValidator(unittest.TestCase): for idx, connection in enumerate(connections): try: ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx) - except n.ValidationError as e: + except n.ValidationError: continue - if 'type' not in connection: + if "type" not in connection: continue - if connection['type'] in ['macvlan']: + if connection["type"] in ["macvlan"]: # initscripts do not support this type. Skip the test. continue - content_current = kwargs.get('initscripts_content_current', None) + content_current = kwargs.get("initscripts_content_current", None) if content_current: content_current = content_current[idx] - c = n.IfcfgUtil.ifcfg_create(connections, idx, content_current = content_current) - #pprint("con[%s] = \"%s\"" % (idx, connections[idx]['name']), c) - exp = kwargs.get('initscripts_dict_expected', None) + c = n.IfcfgUtil.ifcfg_create( + connections, idx, content_current=content_current + ) + # pprint("con[%s] = \"%s\"" % (idx, connections[idx]['name']), c) + exp = kwargs.get("initscripts_dict_expected", None) if exp is not None: self.assertEqual(exp[idx], c) - - def do_connections_validate(self, expected_connections, input_connections, **kwargs): + def do_connections_validate( + self, expected_connections, input_connections, **kwargs + ): connections = ARGS_CONNECTIONS.validate(input_connections) self.assertEqual(expected_connections, connections) self.do_connections_validate_nm(input_connections, **kwargs) @@ -139,24 +165,24 @@ class TestValidator(unittest.TestCase): def test_validate_str(self): - v = n.ArgValidatorStr('state') - self.assertEqual('a', v.validate('a')) - self.assertValidationError(v, 1); - self.assertValidationError(v, None); + v = n.ArgValidatorStr("state") + self.assertEqual("a", v.validate("a")) + self.assertValidationError(v, 1) + self.assertValidationError(v, None) - v = n.ArgValidatorStr('state', required = True) + v = n.ArgValidatorStr("state", required=True) self.assertValidationError(v, None) def test_validate_int(self): - v = n.ArgValidatorNum('state', default_value = None, numeric_type = float) + v = n.ArgValidatorNum("state", default_value=None, numeric_type=float) self.assertEqual(1, v.validate(1)) self.assertEqual(1.5, v.validate(1.5)) self.assertEqual(1.5, v.validate("1.5")) self.assertValidationError(v, None) self.assertValidationError(v, "1a") - v = n.ArgValidatorNum('state', default_value = None) + v = n.ArgValidatorNum("state", default_value=None) self.assertEqual(1, v.validate(1)) self.assertEqual(1, v.validate(1.0)) self.assertEqual(1, v.validate("1")) @@ -165,12 +191,12 @@ class TestValidator(unittest.TestCase): self.assertValidationError(v, 1.5) self.assertValidationError(v, "1.5") - v = n.ArgValidatorNum('state', required = True) + v = n.ArgValidatorNum("state", required=True) self.assertValidationError(v, None) def test_validate_bool(self): - v = n.ArgValidatorBool('state') + v = n.ArgValidatorBool("state") self.assertEqual(True, v.validate("yes")) self.assertEqual(True, v.validate("yeS")) self.assertEqual(True, v.validate("Y")) @@ -192,302 +218,235 @@ class TestValidator(unittest.TestCase): self.assertValidationError(v, "Ye") self.assertValidationError(v, "") self.assertValidationError(v, None) - v = n.ArgValidatorBool('state', required = True) + v = n.ArgValidatorBool("state", required=True) self.assertValidationError(v, None) def test_validate_dict(self): v = n.ArgValidatorDict( - 'dict', - nested = [ - n.ArgValidatorNum('i', required = True), - n.ArgValidatorStr('s', required = False, default_value = 's_default'), - n.ArgValidatorStr('l', required = False, default_value = n.ArgValidator.MISSING), - ]) + "dict", + nested=[ + n.ArgValidatorNum("i", required=True), + n.ArgValidatorStr("s", required=False, default_value="s_default"), + n.ArgValidatorStr( + "l", required=False, default_value=n.ArgValidator.MISSING + ), + ], + ) + self.assertEqual({"i": 5, "s": "s_default"}, v.validate({"i": "5"})) self.assertEqual( - { - 'i': 5, - 's': 's_default', - }, - v.validate({ - 'i': '5', - }) + {"i": 5, "s": "s_default", "l": "6"}, v.validate({"i": "5", "l": "6"}) ) - self.assertEqual( - { - 'i': 5, - 's': 's_default', - 'l': '6', - }, - v.validate({ - 'i': '5', - 'l': '6', - }) - ) - self.assertValidationError(v, { 'k': 1 }) + self.assertValidationError(v, {"k": 1}) def test_validate_list(self): - v = n.ArgValidatorList( - 'list', - nested = n.ArgValidatorNum('i') - ) - self.assertEqual( - [ 1, 5 ], - v.validate([ '1', 5 ]) - ) - self.assertValidationError(v, [1, 's']) + v = n.ArgValidatorList("list", nested=n.ArgValidatorNum("i")) + self.assertEqual([1, 5], v.validate(["1", 5])) + self.assertValidationError(v, [1, "s"]) def test_1(self): self.maxDiff = None - self.do_connections_validate( - [], - [], - ) + self.do_connections_validate([], []) self.do_connections_validate( [ { - 'name': '5', - 'state': 'present', - 'type': 'ethernet', - 'autoconnect': True, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'route_metric6': None, - 'dhcp4_send_hostname': None, - 'dns': [], - 'dns_search': [], + "name": "5", + "state": "present", + "type": "ethernet", + "autoconnect": True, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "route_metric6": None, + "dhcp4_send_hostname": None, + "dns": [], + "dns_search": [], }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'slave_type': None, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "slave_type": None, }, { - 'name': '5', - 'state': 'up', - 'force_state_change': None, - 'wait': None, - 'ignore_errors': None, + "name": "5", + "state": "up", + "force_state_change": None, + "wait": None, + "ignore_errors": None, + }, + ], + [{"name": "5", "type": "ethernet"}, {"name": "5"}], + ) + self.do_connections_validate( + [ + { + "name": "5", + "state": "up", + "type": "ethernet", + "autoconnect": True, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "dns": [], + "dns_search": [], + "route_metric6": None, + "dhcp4_send_hostname": None, + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "force_state_change": None, + "slave_type": None, + "wait": None, } ], - [ - { 'name': '5', - 'type': 'ethernet', - }, - { 'name': '5' } - ], + [{"name": "5", "state": "up", "type": "ethernet"}], ) self.do_connections_validate( [ { - 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': True, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'dns': [], - 'dns_search': [], - 'route_metric6': None, - 'dhcp4_send_hostname': None, + "name": "5", + "state": "up", + "type": "ethernet", + "autoconnect": False, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "dns": [], + "dns_search": [], + "route_metric6": None, + "dhcp4_send_hostname": None, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'slave_type': None, - 'wait': None, - }, - ], - [ - { 'name': '5', - 'state': 'up', - 'type': 'ethernet', - }, - ], - ) - self.do_connections_validate( - [ - { - 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': False, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'dns': [], - 'dns_search': [], - 'route_metric6': None, - 'dhcp4_send_hostname': None, - }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'slave_type': None, - 'wait': None, - }, - ], - [ - { 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': 'no', - }, - ], - initscripts_dict_expected = [ - { - 'ifcfg': { - 'BOOTPROTO': 'dhcp', - 'IPV6INIT': 'yes', - 'IPV6_AUTOCONF': 'yes', - 'NM_CONTROLLED': 'no', - 'ONBOOT': 'no', - 'TYPE': 'Ethernet', - }, - 'keys': None, - 'route': None, - 'route6': None, - 'rule': None, - 'rule6': None, - }, - ], - ) - - self.do_connections_check_invalid([ { 'name': 'a', 'autoconnect': True }]) - - self.do_connections_validate( - [ - { - 'name': '5', - 'state': 'absent', - 'ignore_errors': None, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "force_state_change": None, + "slave_type": None, + "wait": None, } ], - [ + [{"name": "5", "state": "up", "type": "ethernet", "autoconnect": "no"}], + initscripts_dict_expected=[ { - 'name': '5', - 'state': 'absent', + "ifcfg": { + "BOOTPROTO": "dhcp", + "IPV6INIT": "yes", + "IPV6_AUTOCONF": "yes", + "NM_CONTROLLED": "no", + "ONBOOT": "no", + "TYPE": "Ethernet", + }, + "keys": None, + "route": None, + "route6": None, + "rule": None, + "rule6": None, } ], ) + self.do_connections_check_invalid([{"name": "a", "autoconnect": True}]) + + self.do_connections_validate( + [{"name": "5", "state": "absent", "ignore_errors": None}], + [{"name": "5", "state": "absent"}], + ) + self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'prod1', - 'parent': None, - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': True, - 'dns': [], - 'address': [ + "autoconnect": True, + "name": "prod1", + "parent": None, + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": True, + "dns": [], + "address": [ { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.174.5' + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.174.5", } ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], + "route_append_only": False, + "rule_append_only": False, + "route": [], }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'state': 'up', - 'mtu': 1450, - 'check_iface_exists': True, - 'force_state_change': None, - 'mac': '52:54:00:44:9f:ba', - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'type': 'ethernet', - 'slave_type': None, - 'wait': None, - }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "state": "up", + "mtu": 1450, + "check_iface_exists": True, + "force_state_change": None, + "mac": "52:54:00:44:9f:ba", + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "type": "ethernet", + "slave_type": None, + "wait": None, + } ], [ { - 'name': 'prod1', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': 'yes', - 'mac': '52:54:00:44:9f:ba', - 'mtu': 1450, - 'ip': { - 'address': '192.168.174.5/24', - } + "name": "prod1", + "state": "up", + "type": "ethernet", + "autoconnect": "yes", + "mac": "52:54:00:44:9f:ba", + "mtu": 1450, + "ip": {"address": "192.168.174.5/24"}, } ], ) @@ -496,357 +455,807 @@ class TestValidator(unittest.TestCase): self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'prod1', - 'parent': None, - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': True, - 'dns': [{'address': '192.168.174.1', - 'family': socket.AF_INET}], - 'address': [ + "autoconnect": True, + "name": "prod1", + "parent": None, + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": True, + "dns": [{"address": "192.168.174.1", "family": socket.AF_INET}], + "address": [ { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.174.5' + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.174.5", } ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], + "route_append_only": False, + "rule_append_only": False, + "route": [], }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'state': 'up', - 'check_iface_exists': True, - 'force_state_change': None, - 'zone': None, - 'mac': None, - 'master': None, - 'mtu': None, - 'ignore_errors': None, - 'interface_name': None, - 'type': 'ethernet', - 'slave_type': None, - 'wait': None, - }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "state": "up", + "check_iface_exists": True, + "force_state_change": None, + "zone": None, + "mac": None, + "master": None, + "mtu": None, + "ignore_errors": None, + "interface_name": None, + "type": "ethernet", + "slave_type": None, + "wait": None, + } ], [ { - 'name': 'prod1', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': 'yes', - 'ip': { - 'address': '192.168.174.5/24', - 'dns': '192.168.174.1', - } + "name": "prod1", + "state": "up", + "type": "ethernet", + "autoconnect": "yes", + "ip": {"address": "192.168.174.5/24", "dns": "192.168.174.1"}, } ], ) self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'prod1', - 'parent': None, - 'ip': { - 'dhcp4': False, - 'auto6': True, - 'address': [ + "autoconnect": True, + "name": "prod1", + "parent": None, + "ip": { + "dhcp4": False, + "auto6": True, + "address": [ { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.176.5' + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.176.5", }, { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.177.5' + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.177.5", + }, + ], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "dns": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "state": "up", + "mtu": 1450, + "check_iface_exists": True, + "force_state_change": None, + "mac": "52:54:00:44:9f:ba", + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "type": "ethernet", + "slave_type": None, + "wait": None, + }, + { + "autoconnect": True, + "name": "prod.100", + "parent": "prod1", + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": False, + "dns": [], + "address": [ + { + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.174.5", + }, + { + "prefix": 65, + "family": socket.AF_INET6, + "address": "a:b:c::6", + }, + ], + "route_append_only": False, + "rule_append_only": False, + "route": [ + { + "family": socket.AF_INET, + "network": "192.168.5.0", + "prefix": 24, + "gateway": None, + "metric": -1, } ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'dns': [] }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'state': 'up', - 'mtu': 1450, - 'check_iface_exists': True, - 'force_state_change': None, - 'mac': '52:54:00:44:9f:ba', - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'type': 'ethernet', - 'slave_type': None, - 'wait': None, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "slave_type": None, + "ignore_errors": None, + "interface_name": "prod.100", + "type": "vlan", + "vlan": {"id": 100}, + "wait": None, }, - { - 'autoconnect': True, - 'name': 'prod.100', - 'parent': 'prod1', - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': False, - 'dns': [], - 'address': [ - { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.174.5' - }, - { - 'prefix': 65, - 'family': socket.AF_INET6, - 'address': 'a:b:c::6', - }, - ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'family': socket.AF_INET, - 'network': '192.168.5.0', - 'prefix': 24, - 'gateway': None, - 'metric': -1, - }, - ], - }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'slave_type': None, - 'ignore_errors': None, - 'interface_name': 'prod.100', - 'type': 'vlan', - 'vlan': { - 'id' : 100, - }, - 'wait': None, - } ], [ { - 'name': 'prod1', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': 'yes', - 'mac': '52:54:00:44:9f:ba', - 'mtu': 1450, - 'ip': { - 'address': '192.168.176.5/24 192.168.177.5/24', - } + "name": "prod1", + "state": "up", + "type": "ethernet", + "autoconnect": "yes", + "mac": "52:54:00:44:9f:ba", + "mtu": 1450, + "ip": {"address": "192.168.176.5/24 192.168.177.5/24"}, }, { - 'name': 'prod.100', - 'state': 'up', - 'type': 'vlan', - 'parent': 'prod1', - 'vlan': { - 'id': '100', + "name": "prod.100", + "state": "up", + "type": "vlan", + "parent": "prod1", + "vlan": {"id": "100"}, + "ip": { + "address": [ + "192.168.174.5/24", + {"address": "a:b:c::6", "prefix": 65}, + ], + "route_append_only": False, + "rule_append_only": False, + "route": [{"network": "192.168.5.0"}], }, - 'ip': { - 'address': [ - '192.168.174.5/24', - { - 'address': 'a:b:c::6', - 'prefix': 65, - }, - ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'network': '192.168.5.0', - }, - ], - } - } + }, ], ) self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'prod1', - 'parent': None, - 'ip': { - 'dhcp4': False, - 'auto6': True, - 'address': [ + "autoconnect": True, + "name": "prod1", + "parent": None, + "ip": { + "dhcp4": False, + "auto6": True, + "address": [ { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.176.5' + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.176.5", }, { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.177.5' + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.177.5", + }, + ], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "dns": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "state": "up", + "mtu": 1450, + "check_iface_exists": True, + "force_state_change": None, + "mac": "52:54:00:44:9f:ba", + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "type": "ethernet", + "slave_type": None, + "wait": None, + }, + { + "autoconnect": True, + "name": "prod.100", + "parent": "prod1", + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": False, + "dns": [], + "address": [ + { + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.174.5", + }, + { + "prefix": 65, + "family": socket.AF_INET6, + "address": "a:b:c::6", + }, + ], + "route_append_only": False, + "rule_append_only": False, + "route": [ + { + "family": socket.AF_INET, + "network": "192.168.5.0", + "prefix": 24, + "gateway": None, + "metric": -1, } ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'dns': [] }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'state': 'up', - 'mtu': 1450, - 'check_iface_exists': True, - 'force_state_change': None, - 'mac': '52:54:00:44:9f:ba', - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'type': 'ethernet', - 'slave_type': None, - 'wait': None, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "slave_type": None, + "ignore_errors": None, + "interface_name": "prod.100", + "type": "vlan", + "vlan": {"id": 101}, + "wait": None, + }, + ], + [ + { + "name": "prod1", + "state": "up", + "type": "ethernet", + "autoconnect": "yes", + "mac": "52:54:00:44:9f:ba", + "mtu": 1450, + "ip": {"address": "192.168.176.5/24 192.168.177.5/24"}, }, { - 'autoconnect': True, - 'name': 'prod.100', - 'parent': 'prod1', - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': False, - 'dns': [], - 'address': [ - { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.174.5' - }, - { - 'prefix': 65, - 'family': socket.AF_INET6, - 'address': 'a:b:c::6', - }, + "name": "prod.100", + "state": "up", + "type": "vlan", + "parent": "prod1", + "vlan_id": 101, + "ip": { + "address": [ + "192.168.174.5/24", + {"address": "a:b:c::6", "prefix": 65}, ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ + "route_append_only": False, + "rule_append_only": False, + "route": [{"network": "192.168.5.0"}], + }, + }, + ], + ) + + self.do_connections_validate( + [ + { + "autoconnect": True, + "name": "eth0-parent", + "parent": None, + "ip": { + "dhcp4": False, + "auto6": False, + "address": [ { - 'family': socket.AF_INET, - 'network': '192.168.5.0', - 'prefix': 24, - 'gateway': None, - 'metric': -1, - }, + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.122.3", + } + ], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "dns": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "state": "up", + "mtu": 1450, + "check_iface_exists": True, + "force_state_change": None, + "mac": "33:24:10:24:2f:b9", + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": "eth0", + "type": "ethernet", + "slave_type": None, + "wait": None, + }, + { + "autoconnect": True, + "name": "veth0.0", + "parent": "eth0-parent", + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": False, + "dns": [], + "address": [ + { + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.244.1", + } + ], + "route_append_only": False, + "rule_append_only": False, + "route": [ + { + "family": socket.AF_INET, + "network": "192.168.244.0", + "prefix": 24, + "gateway": None, + "metric": -1, + } ], }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "slave_type": None, + "ignore_errors": None, + "interface_name": "veth0", + "type": "macvlan", + "macvlan": {"mode": "bridge", "promiscuous": True, "tap": False}, + "wait": None, + }, + { + "autoconnect": True, + "name": "veth0.1", + "parent": "eth0-parent", + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": False, + "dns": [], + "address": [ + { + "prefix": 24, + "family": socket.AF_INET, + "address": "192.168.245.7", + } + ], + "route_append_only": False, + "rule_append_only": False, + "route": [ + { + "family": socket.AF_INET, + "network": "192.168.245.0", + "prefix": 24, + "gateway": None, + "metric": -1, + } + ], }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'slave_type': None, - 'ignore_errors': None, - 'interface_name': 'prod.100', - 'type': 'vlan', - 'vlan': { - 'id' : 101, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "slave_type": None, + "ignore_errors": None, + "interface_name": "veth1", + "type": "macvlan", + "macvlan": {"mode": "passthru", "promiscuous": False, "tap": True}, + "wait": None, + }, + ], + [ + { + "name": "eth0-parent", + "state": "up", + "type": "ethernet", + "autoconnect": "yes", + "interface_name": "eth0", + "mac": "33:24:10:24:2f:b9", + "mtu": 1450, + "ip": {"address": "192.168.122.3/24", "auto6": False}, + }, + { + "name": "veth0.0", + "state": "up", + "type": "macvlan", + "parent": "eth0-parent", + "interface_name": "veth0", + "macvlan": {"mode": "bridge", "promiscuous": True, "tap": False}, + "ip": { + "address": "192.168.244.1/24", + "auto6": False, + "route_append_only": False, + "rule_append_only": False, + "route": [{"network": "192.168.244.0"}], }, - 'wait': None, + }, + { + "name": "veth0.1", + "state": "up", + "type": "macvlan", + "parent": "eth0-parent", + "interface_name": "veth1", + "macvlan": {"mode": "passthru", "promiscuous": False, "tap": True}, + "ip": { + "address": "192.168.245.7/24", + "auto6": False, + "route_append_only": False, + "rule_append_only": False, + "route": [{"network": "192.168.245.0"}], + }, + }, + ], + ) + + self.do_connections_validate( + [ + { + "autoconnect": True, + "name": "prod2", + "parent": None, + "ip": { + "dhcp4": False, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": False, + "dns": [], + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "ignore_errors": None, + "interface_name": "bridge2", + "type": "bridge", + "slave_type": None, + "wait": None, + }, + { + "autoconnect": True, + "name": "prod2-slave1", + "parent": None, + "ip": { + "dhcp4": True, + "auto6": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "dns": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": "prod2", + "ignore_errors": None, + "interface_name": "eth1", + "type": "ethernet", + "slave_type": "bridge", + "wait": None, + }, + ], + [ + { + "name": "prod2", + "state": "up", + "type": "bridge", + "interface_name": "bridge2", + "ip": {"dhcp4": False, "auto6": False}, + }, + { + "name": "prod2-slave1", + "state": "up", + "type": "ethernet", + "interface_name": "eth1", + "master": "prod2", + }, + ], + ) + + self.do_connections_validate( + [ + { + "autoconnect": True, + "name": "bond1", + "parent": None, + "ip": { + "dhcp4": True, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": True, + "dns": [], + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "ignore_errors": None, + "interface_name": "bond1", + "type": "bond", + "slave_type": None, + "bond": {"mode": "balance-rr", "miimon": None}, + "wait": None, + } + ], + [{"name": "bond1", "state": "up", "type": "bond"}], + ) + + self.do_connections_validate( + [ + { + "autoconnect": True, + "name": "bond1", + "parent": None, + "ip": { + "dhcp4": True, + "route_metric6": None, + "route_metric4": None, + "dns_search": [], + "dhcp4_send_hostname": None, + "gateway6": None, + "gateway4": None, + "auto6": True, + "dns": [], + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "check_iface_exists": True, + "force_state_change": None, + "state": "up", + "master": None, + "ignore_errors": None, + "interface_name": "bond1", + "type": "bond", + "slave_type": None, + "bond": {"mode": "active-backup", "miimon": None}, + "wait": None, } ], [ { - 'name': 'prod1', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': 'yes', - 'mac': '52:54:00:44:9f:ba', - 'mtu': 1450, - 'ip': { - 'address': '192.168.176.5/24 192.168.177.5/24', - } - }, + "name": "bond1", + "state": "up", + "type": "bond", + "bond": {"mode": "active-backup"}, + } + ], + ) + + self.do_connections_check_invalid([{}]) + self.do_connections_check_invalid([{"name": "b", "xxx": 5}]) + + self.do_connections_validate( + [ { - 'name': 'prod.100', - 'state': 'up', - 'type': 'vlan', - 'parent': 'prod1', - 'vlan_id': 101, - 'ip': { - 'address': [ - '192.168.174.5/24', - { - 'address': 'a:b:c::6', - 'prefix': 65, - }, - ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'network': '192.168.5.0', - }, - ], - } + "autoconnect": True, + "interface_name": None, + "ip": { + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "auto6": True, + "dhcp4": True, + "dhcp4_send_hostname": None, + "gateway4": None, + "gateway6": None, + "route_metric4": None, + "route_metric6": None, + "dns": [], + "dns_search": [], + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": "aa:bb:cc:dd:ee:ff", + "mtu": None, + "zone": None, + "master": None, + "check_iface_exists": True, + "name": "5", + "parent": None, + "ignore_errors": None, + "slave_type": None, + "state": "present", + "type": "ethernet", + } + ], + [{"name": "5", "type": "ethernet", "mac": "AA:bb:cC:DD:ee:FF"}], + ) + + self.do_connections_validate( + [ + { + "name": "5", + "state": "up", + "type": "ethernet", + "autoconnect": True, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "dns": [], + "dns_search": [], + "route_metric6": None, + "dhcp4_send_hostname": None, + }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "force_state_change": None, + "slave_type": None, + "wait": None, + } + ], + [{"name": "5", "state": "up", "type": "ethernet"}], + ) + + self.do_connections_validate( + [ + { + "name": "5", + "state": "up", + "type": "ethernet", + "autoconnect": True, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [], + "dns": [], + "dns_search": [], + "route_metric6": None, + "dhcp4_send_hostname": None, + }, + "ethernet": {"autoneg": False, "duplex": "half", "speed": 400}, + "mac": None, + "mtu": None, + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "force_state_change": None, + "slave_type": None, + "wait": None, + } + ], + [ + { + "name": "5", + "state": "up", + "type": "ethernet", + "ip": {}, + "ethernet": {"duplex": "half", "speed": 400}, + } + ], + initscripts_dict_expected=[ + { + "ifcfg": { + "BOOTPROTO": "dhcp", + "ETHTOOL_OPTS": "autoneg off speed 400 duplex half", + "IPV6INIT": "yes", + "IPV6_AUTOCONF": "yes", + "NM_CONTROLLED": "no", + "ONBOOT": "yes", + "TYPE": "Ethernet", + }, + "keys": None, + "route": None, + "route6": None, + "rule": None, + "rule6": None, } ], ) @@ -854,309 +1263,136 @@ class TestValidator(unittest.TestCase): self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'eth0-parent', - 'parent': None, - 'ip': { - 'dhcp4': False, - 'auto6': False, - 'address': [ - { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.122.3' - }, - ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'dns': [] + "autoconnect": True, + "check_iface_exists": True, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "force_state_change": None, + "ignore_errors": None, + "interface_name": "6643-master", + "ip": { + "address": [], + "auto6": True, + "dhcp4": True, + "dhcp4_send_hostname": None, + "dns": [], + "dns_search": [], + "gateway4": None, + "gateway6": None, + "route": [], + "route_append_only": False, + "route_metric4": None, + "route_metric6": None, + "rule_append_only": False, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'state': 'up', - 'mtu': 1450, - 'check_iface_exists': True, - 'force_state_change': None, - 'mac': '33:24:10:24:2f:b9', - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': "eth0", - 'type': 'ethernet', - 'slave_type': None, - 'wait': None, + "mac": None, + "master": None, + "mtu": None, + "name": "6643-master", + "parent": None, + "slave_type": None, + "state": "up", + "type": "bridge", + "wait": None, + "zone": None, }, { - 'autoconnect': True, - 'name': 'veth0.0', - 'parent': 'eth0-parent', - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': False, - 'dns': [], - 'address': [ - { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.244.1' - }, - ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'family': socket.AF_INET, - 'network': '192.168.244.0', - 'prefix': 24, - 'gateway': None, - 'metric': -1, - }, - ], + "autoconnect": True, + "check_iface_exists": True, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "force_state_change": None, + "ignore_errors": None, + "interface_name": None, + "ip": { + "address": [], + "auto6": True, + "dhcp4": True, + "dhcp4_send_hostname": None, + "dns": [], + "dns_search": [], + "gateway4": None, + "gateway6": None, + "route": [], + "route_append_only": False, + "route_metric4": None, + "route_metric6": None, + "rule_append_only": False, }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'slave_type': None, - 'ignore_errors': None, - 'interface_name': 'veth0', - 'type': 'macvlan', - 'macvlan': { - 'mode' : 'bridge', - 'promiscuous': True, - 'tap': False, - }, - 'wait': None, + "mac": None, + "master": "6643-master", + "mtu": None, + "name": "6643", + "parent": None, + "slave_type": "bridge", + "state": "up", + "type": "ethernet", + "wait": None, + "zone": None, }, - { - 'autoconnect': True, - 'name': 'veth0.1', - 'parent': 'eth0-parent', - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': False, - 'dns': [], - 'address': [ - { - 'prefix': 24, - 'family': socket.AF_INET, - 'address': '192.168.245.7' - }, - ], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'family': socket.AF_INET, - 'network': '192.168.245.0', - 'prefix': 24, - 'gateway': None, - 'metric': -1, - }, - ], - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'slave_type': None, - 'ignore_errors': None, - 'interface_name': 'veth1', - 'type': 'macvlan', - 'macvlan': { - 'mode' : 'passthru', - 'promiscuous': False, - 'tap': True, - }, - 'wait': None, - } ], [ + {"name": "6643-master", "state": "up", "type": "bridge"}, { - 'name': 'eth0-parent', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': 'yes', - 'interface_name': 'eth0', - 'mac': '33:24:10:24:2f:b9', - 'mtu': 1450, - 'ip': { - 'address': '192.168.122.3/24', - 'auto6': False - }, + "name": "6643", + "state": "up", + "type": "ethernet", + "master": "6643-master", }, - { - 'name': 'veth0.0', - 'state': 'up', - 'type': 'macvlan', - 'parent': 'eth0-parent', - 'interface_name': 'veth0', - 'macvlan': { - 'mode': 'bridge', - 'promiscuous': True, - 'tap': False, - }, - 'ip': { - 'address': '192.168.244.1/24', - 'auto6': False, - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'network': '192.168.244.0', - }, - ], - } - }, - { - 'name': 'veth0.1', - 'state': 'up', - 'type': 'macvlan', - 'parent': 'eth0-parent', - 'interface_name': 'veth1', - 'macvlan': { - 'mode': 'passthru', - 'promiscuous': False, - 'tap': True - }, - 'ip': { - 'address': '192.168.245.7/24', - 'auto6': False, - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ - { - 'network': '192.168.245.0', - }, - ], - } - } ], ) - self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'prod2', - 'parent': None, - 'ip': { - 'dhcp4': False, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': False, - 'dns': [], - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], + "autoconnect": True, + "check_iface_exists": True, + "force_state_change": None, + "ignore_errors": None, + "infiniband": {"p_key": -1, "transport_mode": "datagram"}, + "interface_name": None, + "ip": { + "address": [], + "auto6": True, + "dhcp4": True, + "dhcp4_send_hostname": None, + "dns": [], + "dns_search": [], + "gateway4": None, + "gateway6": None, + "route": [], + "route_append_only": False, + "route_metric4": None, + "route_metric6": None, + "rule_append_only": False, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'ignore_errors': None, - 'interface_name': 'bridge2', - 'type': 'bridge', - 'slave_type': None, - 'wait': None, - }, - { - 'autoconnect': True, - 'name': 'prod2-slave1', - 'parent': None, - 'ip': { - 'dhcp4': True, - 'auto6': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'dns': [] - }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': 'prod2', - 'ignore_errors': None, - 'interface_name': 'eth1', - 'type': 'ethernet', - 'slave_type': 'bridge', - 'wait': None, + "mac": None, + "master": None, + "mtu": None, + "name": "infiniband.1", + "parent": None, + "slave_type": None, + "state": "up", + "type": "infiniband", + "wait": None, + "zone": None, } ], - [ + [{"name": "infiniband.1", "state": "up", "type": "infiniband"}], + initscripts_dict_expected=[ { - 'name': 'prod2', - 'state': 'up', - 'type': 'bridge', - 'interface_name': 'bridge2', - 'ip': { - 'dhcp4': False, - 'auto6': False, + "ifcfg": { + "BOOTPROTO": "dhcp", + "CONNECTED_MODE": "no", + "IPV6INIT": "yes", + "IPV6_AUTOCONF": "yes", + "NM_CONTROLLED": "no", + "ONBOOT": "yes", + "TYPE": "InfiniBand", }, - }, - { - 'name': 'prod2-slave1', - 'state': 'up', - 'type': 'ethernet', - 'interface_name': 'eth1', - 'master': 'prod2', + "keys": None, + "route": None, + "route6": None, + "rule": None, + "rule6": None, } ], ) @@ -1164,158 +1400,70 @@ class TestValidator(unittest.TestCase): self.do_connections_validate( [ { - 'autoconnect': True, - 'name': 'bond1', - 'parent': None, - 'ip': { - 'dhcp4': True, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': True, - 'dns': [], - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], + "autoconnect": True, + "check_iface_exists": True, + "force_state_change": None, + "ignore_errors": None, + "infiniband": {"p_key": 5, "transport_mode": "datagram"}, + "interface_name": None, + "ip": { + "address": [], + "auto6": True, + "dhcp4": True, + "dhcp4_send_hostname": None, + "dns": [], + "dns_search": [], + "gateway4": None, + "gateway6": None, + "route": [], + "route_append_only": False, + "route_metric4": None, + "route_metric6": None, + "rule_append_only": False, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'ignore_errors': None, - 'interface_name': 'bond1', - 'type': 'bond', - 'slave_type': None, - 'bond': { - 'mode': 'balance-rr', - 'miimon': None, - }, - 'wait': None, - }, + "mac": "11:22:33:44:55:66:77:88:99:00:" + "11:22:33:44:55:66:77:88:99:00", + "master": None, + "mtu": None, + "name": "infiniband.2", + "parent": None, + "slave_type": None, + "state": "up", + "type": "infiniband", + "wait": None, + "zone": None, + } ], [ { - 'name': 'bond1', - 'state': 'up', - 'type': 'bond', - }, + "name": "infiniband.2", + "state": "up", + "type": "infiniband", + "mac": "11:22:33:44:55:66:77:88:99:00:" + "11:22:33:44:55:66:77:88:99:00", + "infiniband_p_key": 5, + } ], - ) - - self.do_connections_validate( - [ + initscripts_dict_expected=[ { - 'autoconnect': True, - 'name': 'bond1', - 'parent': None, - 'ip': { - 'dhcp4': True, - 'route_metric6': None, - 'route_metric4': None, - 'dns_search': [], - 'dhcp4_send_hostname': None, - 'gateway6': None, - 'gateway4': None, - 'auto6': True, - 'dns': [], - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], + "ifcfg": { + "BOOTPROTO": "dhcp", + "CONNECTED_MODE": "no", + "HWADDR": "11:22:33:44:55:66:77:88:99:00:" + "11:22:33:44:55:66:77:88:99:00", + "IPV6INIT": "yes", + "IPV6_AUTOCONF": "yes", + "NM_CONTROLLED": "no", + "ONBOOT": "yes", + "PKEY": "yes", + "PKEY_ID": "5", + "TYPE": "InfiniBand", }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'state': 'up', - 'master': None, - 'ignore_errors': None, - 'interface_name': 'bond1', - 'type': 'bond', - 'slave_type': None, - 'bond': { - 'mode': 'active-backup', - 'miimon': None, - }, - 'wait': None, - }, - ], - [ - { - 'name': 'bond1', - 'state': 'up', - 'type': 'bond', - 'bond': { - 'mode': 'active-backup', - }, - }, - ], - ) - - self.do_connections_check_invalid([ { } ]) - self.do_connections_check_invalid([ { 'name': 'b', 'xxx': 5 } ]) - - self.do_connections_validate( - [ - { - 'autoconnect': True, - 'interface_name': None, - 'ip': { - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'auto6': True, - 'dhcp4': True, - 'dhcp4_send_hostname': None, - 'gateway4': None, - 'gateway6': None, - 'route_metric4': None, - 'route_metric6': None, - 'dns': [], - 'dns_search': [], - }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': 'aa:bb:cc:dd:ee:ff', - 'mtu': None, - 'zone': None, - 'master': None, - 'check_iface_exists': True, - 'name': '5', - 'parent': None, - 'ignore_errors': None, - 'slave_type': None, - 'state': 'present', - 'type': 'ethernet', - }, - ], - [ - { - 'name': '5', - 'type': 'ethernet', - 'mac': 'AA:bb:cC:DD:ee:FF', + "keys": None, + "route": None, + "route6": None, + "rule": None, + "rule6": None, } ], ) @@ -1323,609 +1471,219 @@ class TestValidator(unittest.TestCase): self.do_connections_validate( [ { - 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': True, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'dns': [], - 'dns_search': [ ], - 'route_metric6': None, - 'dhcp4_send_hostname': None, + "name": "555", + "state": "up", + "type": "ethernet", + "autoconnect": True, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": False, + "rule_append_only": False, + "route": [ + { + "family": socket.AF_INET, + "network": "192.168.45.0", + "prefix": 24, + "gateway": None, + "metric": 545, + }, + { + "family": socket.AF_INET, + "network": "192.168.46.0", + "prefix": 30, + "gateway": None, + "metric": -1, + }, + ], + "dns": [], + "dns_search": ["aa", "bb"], + "route_metric6": None, + "dhcp4_send_hostname": None, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'slave_type': None, - 'wait': None, - }, - ], - [ - { 'name': '5', - 'state': 'up', - 'type': 'ethernet', - }, - ], - ) - - self.do_connections_validate( - [ - { - 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': True, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [], - 'dns': [], - 'dns_search': [ ], - 'route_metric6': None, - 'dhcp4_send_hostname': None, - }, - 'ethernet': { - 'autoneg': False, - 'duplex': 'half', - 'speed': 400, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'slave_type': None, - 'wait': None, - }, - ], - [ - { - 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'ip': { - }, - 'ethernet': { - 'duplex': 'half', - 'speed': 400, - }, - }, - ], - initscripts_dict_expected = [ - { - 'ifcfg': { - 'BOOTPROTO': 'dhcp', - 'ETHTOOL_OPTS': 'autoneg off speed 400 duplex half', - 'IPV6INIT': 'yes', - 'IPV6_AUTOCONF': 'yes', - 'NM_CONTROLLED': 'no', - 'ONBOOT': 'yes', - 'TYPE': 'Ethernet', - }, - 'keys': None, - 'route': None, - 'route6': None, - 'rule': None, - 'rule6': None, - }, - ], - ) - - self.do_connections_validate( - [ - { - 'autoconnect': True, - 'check_iface_exists': True, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'force_state_change': None, - 'ignore_errors': None, - 'interface_name': '6643-master', - 'ip': { - 'address': [], - 'auto6': True, - 'dhcp4': True, - 'dhcp4_send_hostname': None, - 'dns': [], - 'dns_search': [], - 'gateway4': None, - 'gateway6': None, - 'route': [], - 'route_append_only': False, - 'route_metric4': None, - 'route_metric6': None, - 'rule_append_only': False - }, - 'mac': None, - 'master': None, - 'mtu': None, - 'name': '6643-master', - 'parent': None, - 'slave_type': None, - 'state': 'up', - 'type': 'bridge', - 'wait': None, - 'zone': None, - }, - { - 'autoconnect': True, - 'check_iface_exists': True, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'force_state_change': None, - 'ignore_errors': None, - 'interface_name': None, - 'ip': { - 'address': [], - 'auto6': True, - 'dhcp4': True, - 'dhcp4_send_hostname': None, - 'dns': [], - 'dns_search': [], - 'gateway4': None, - 'gateway6': None, - 'route': [], - 'route_append_only': False, - 'route_metric4': None, - 'route_metric6': None, - 'rule_append_only': False, - }, - 'mac': None, - 'master': '6643-master', - 'mtu': None, - 'name': '6643', - 'parent': None, - 'slave_type': 'bridge', - 'state': 'up', - 'type': 'ethernet', - 'wait': None, - 'zone': None, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": None, + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "force_state_change": None, + "slave_type": None, + "wait": None, } ], [ - { 'name': '6643-master', - 'state': 'up', - 'type': 'bridge', - }, - { 'name': '6643', - 'state': 'up', - 'type': 'ethernet', - 'master': '6643-master', - }, + { + "name": "555", + "state": "up", + "type": "ethernet", + "ip": { + "dns_search": ["aa", "bb"], + "route": [ + {"network": "192.168.45.0", "metric": 545}, + {"network": "192.168.46.0", "prefix": 30}, + ], + }, + } + ], + initscripts_dict_expected=[ + { + "ifcfg": { + "BOOTPROTO": "dhcp", + "DOMAIN": "aa bb", + "IPV6INIT": "yes", + "IPV6_AUTOCONF": "yes", + "NM_CONTROLLED": "no", + "ONBOOT": "yes", + "TYPE": "Ethernet", + }, + "keys": None, + "route": "192.168.45.0/24 metric 545\n192.168.46.0/30\n", + "route6": None, + "rule": None, + "rule6": None, + } ], ) self.do_connections_validate( [ { - 'autoconnect': True, - 'check_iface_exists': True, - 'force_state_change': None, - 'ignore_errors': None, - 'infiniband': { - 'p_key': -1, - 'transport_mode': 'datagram', - }, - 'interface_name': None, - 'ip': { - 'address': [], - 'auto6': True, - 'dhcp4': True, - 'dhcp4_send_hostname': None, - 'dns': [], - 'dns_search': [], - 'gateway4': None, - 'gateway6': None, - 'route': [], - 'route_append_only': False, - 'route_metric4': None, - 'route_metric6': None, - 'rule_append_only': False - }, - 'mac': None, - 'master': None, - 'mtu': None, - 'name': 'infiniband.1', - 'parent': None, - 'slave_type': None, - 'state': 'up', - 'type': 'infiniband', - 'wait': None, - 'zone': None, - }, - ], - [ - { - 'name': 'infiniband.1', - 'state': 'up', - 'type': 'infiniband', - }, - ], - initscripts_dict_expected = [ - { - 'ifcfg': { - 'BOOTPROTO': 'dhcp', - 'CONNECTED_MODE': 'no', - 'IPV6INIT': 'yes', - 'IPV6_AUTOCONF': 'yes', - 'NM_CONTROLLED': 'no', - 'ONBOOT': 'yes', - 'TYPE': 'InfiniBand', - }, - 'keys': None, - 'route': None, - 'route6': None, - 'rule': None, - 'rule6': None, - }, - ], - ) - - self.do_connections_validate( - [ - { - 'autoconnect': True, - 'check_iface_exists': True, - 'force_state_change': None, - 'ignore_errors': None, - 'infiniband': { - 'p_key': 5, - 'transport_mode': 'datagram', - }, - 'interface_name': None, - 'ip': { - 'address': [], - 'auto6': True, - 'dhcp4': True, - 'dhcp4_send_hostname': None, - 'dns': [], - 'dns_search': [], - 'gateway4': None, - 'gateway6': None, - 'route': [], - 'route_append_only': False, - 'route_metric4': None, - 'route_metric6': None, - 'rule_append_only': False - }, - 'mac': '11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00', - 'master': None, - 'mtu': None, - 'name': 'infiniband.2', - 'parent': None, - 'slave_type': None, - 'state': 'up', - 'type': 'infiniband', - 'wait': None, - 'zone': None, - }, - ], - [ - { - 'name': 'infiniband.2', - 'state': 'up', - 'type': 'infiniband', - 'mac': '11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00', - 'infiniband_p_key': 5, - }, - ], - initscripts_dict_expected = [ - { - 'ifcfg': { - 'BOOTPROTO': 'dhcp', - 'CONNECTED_MODE': 'no', - 'HWADDR': '11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66:77:88:99:00', - 'IPV6INIT': 'yes', - 'IPV6_AUTOCONF': 'yes', - 'NM_CONTROLLED': 'no', - 'ONBOOT': 'yes', - 'PKEY': 'yes', - 'PKEY_ID': '5', - 'TYPE': 'InfiniBand', - }, - 'keys': None, - 'route': None, - 'route6': None, - 'rule': None, - 'rule6': None, - }, - ], - ) - - - self.do_connections_validate( - [ - { - 'name': '555', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': True, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': False, - 'rule_append_only': False, - 'route': [ + "name": "e556", + "state": "up", + "type": "ethernet", + "autoconnect": True, + "parent": None, + "ip": { + "gateway6": None, + "gateway4": None, + "route_metric4": None, + "auto6": True, + "dhcp4": True, + "address": [], + "route_append_only": True, + "rule_append_only": False, + "route": [ { - 'family': socket.AF_INET, - 'network': '192.168.45.0', - 'prefix': 24, - 'gateway': None, - 'metric': 545, + "family": socket.AF_INET, + "network": "192.168.45.0", + "prefix": 24, + "gateway": None, + "metric": 545, }, { - 'family': socket.AF_INET, - 'network': '192.168.46.0', - 'prefix': 30, - 'gateway': None, - 'metric': -1, + "family": socket.AF_INET, + "network": "192.168.46.0", + "prefix": 30, + "gateway": None, + "metric": -1, + }, + { + "family": socket.AF_INET6, + "network": "a:b:c:d::", + "prefix": 64, + "gateway": None, + "metric": -1, }, ], - 'dns': [], - 'dns_search': [ 'aa', 'bb' ], - 'route_metric6': None, - 'dhcp4_send_hostname': None, + "dns": [], + "dns_search": ["aa", "bb"], + "route_metric6": None, + "dhcp4_send_hostname": None, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': None, - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'slave_type': None, - 'wait': None, - }, + "ethernet": {"autoneg": None, "duplex": None, "speed": 0}, + "mac": None, + "mtu": None, + "zone": "external", + "master": None, + "ignore_errors": None, + "interface_name": None, + "check_iface_exists": True, + "force_state_change": None, + "slave_type": None, + "wait": None, + } ], [ - { 'name': '555', - 'state': 'up', - 'type': 'ethernet', - 'ip': { - 'dns_search': [ 'aa', 'bb' ], - 'route': [ - { - 'network': '192.168.45.0', - 'metric': 545, - }, - { - 'network': '192.168.46.0', - 'prefix': 30, - }, - ], - }, - }, - ], - initscripts_dict_expected = [ { - 'ifcfg': { - 'BOOTPROTO': 'dhcp', - 'DOMAIN': 'aa bb', - 'IPV6INIT': 'yes', - 'IPV6_AUTOCONF': 'yes', - 'NM_CONTROLLED': 'no', - 'ONBOOT': 'yes', - 'TYPE': 'Ethernet', - }, - 'keys': None, - 'route': '192.168.45.0/24 metric 545\n192.168.46.0/30\n', - 'route6': None, - 'rule': None, - 'rule6': None, - }, - ], - ) - - self.do_connections_validate( - [ - { - 'name': 'e556', - 'state': 'up', - 'type': 'ethernet', - 'autoconnect': True, - 'parent': None, - 'ip': { - 'gateway6': None, - 'gateway4': None, - 'route_metric4': None, - 'auto6': True, - 'dhcp4': True, - 'address': [], - 'route_append_only': True, - 'rule_append_only': False, - 'route': [ - { - 'family': socket.AF_INET, - 'network': '192.168.45.0', - 'prefix': 24, - 'gateway': None, - 'metric': 545, - }, - { - 'family': socket.AF_INET, - 'network': '192.168.46.0', - 'prefix': 30, - 'gateway': None, - 'metric': -1, - }, - { - 'family': socket.AF_INET6, - 'network': 'a:b:c:d::', - 'prefix': 64, - 'gateway': None, - 'metric': -1, - }, + "name": "e556", + "state": "up", + "type": "ethernet", + "zone": "external", + "ip": { + "dns_search": ["aa", "bb"], + "route_append_only": True, + "rule_append_only": False, + "route": [ + {"network": "192.168.45.0", "metric": 545}, + {"network": "192.168.46.0", "prefix": 30}, + {"network": "a:b:c:d::"}, ], - 'dns': [], - 'dns_search': [ 'aa', 'bb' ], - 'route_metric6': None, - 'dhcp4_send_hostname': None, }, - 'ethernet': { - 'autoneg': None, - 'duplex': None, - 'speed': 0, - }, - 'mac': None, - 'mtu': None, - 'zone': 'external', - 'master': None, - 'ignore_errors': None, - 'interface_name': None, - 'check_iface_exists': True, - 'force_state_change': None, - 'slave_type': None, - 'wait': None, - }, + } ], - [ - { 'name': 'e556', - 'state': 'up', - 'type': 'ethernet', - 'zone': 'external', - 'ip': { - 'dns_search': [ 'aa', 'bb' ], - 'route_append_only': True, - 'rule_append_only': False, - 'route': [ - { - 'network': '192.168.45.0', - 'metric': 545, - }, - { - 'network': '192.168.46.0', - 'prefix': 30, - }, - { - 'network': 'a:b:c:d::', - }, - ], - }, - }, - ], - nm_route_list_current = [ + nm_route_list_current=[ [ - { - 'network': '192.168.40.0', - 'prefix': 24, - 'metric': 545, - }, - { - 'network': '192.168.46.0', - 'prefix': 30, - }, - { - 'network': 'a:b:c:f::', - }, - ], + {"network": "192.168.40.0", "prefix": 24, "metric": 545}, + {"network": "192.168.46.0", "prefix": 30}, + {"network": "a:b:c:f::"}, + ] ], - nm_route_list_expected = [ + nm_route_list_expected=[ [ - { - 'network': '192.168.40.0', - 'prefix': 24, - 'metric': 545, - }, - { - 'network': '192.168.46.0', - 'prefix': 30, - }, - { - 'network': '192.168.45.0', - 'prefix': 24, - 'metric': 545, - }, - { - 'network': 'a:b:c:f::', - }, - { - 'network': 'a:b:c:d::', - }, - ], + {"network": "192.168.40.0", "prefix": 24, "metric": 545}, + {"network": "192.168.46.0", "prefix": 30}, + {"network": "192.168.45.0", "prefix": 24, "metric": 545}, + {"network": "a:b:c:f::"}, + {"network": "a:b:c:d::"}, + ] ], - initscripts_content_current = [ + initscripts_content_current=[ { - 'ifcfg': '', - 'keys': None, - 'route': '192.168.40.0/24 metric 545\n192.168.46.0/30', - 'route6': 'a:b:c:f::/64', - 'rule': None, - 'rule6': None, - }, + "ifcfg": "", + "keys": None, + "route": "192.168.40.0/24 metric 545\n192.168.46.0/30", + "route6": "a:b:c:f::/64", + "rule": None, + "rule6": None, + } ], - initscripts_dict_expected = [ + initscripts_dict_expected=[ { - 'ifcfg': { - 'BOOTPROTO': 'dhcp', - 'DOMAIN': 'aa bb', - 'IPV6INIT': 'yes', - 'IPV6_AUTOCONF': 'yes', - 'NM_CONTROLLED': 'no', - 'ONBOOT': 'yes', - 'TYPE': 'Ethernet', - 'ZONE': 'external', + "ifcfg": { + "BOOTPROTO": "dhcp", + "DOMAIN": "aa bb", + "IPV6INIT": "yes", + "IPV6_AUTOCONF": "yes", + "NM_CONTROLLED": "no", + "ONBOOT": "yes", + "TYPE": "Ethernet", + "ZONE": "external", }, - 'keys': None, - 'route': '192.168.40.0/24 metric 545\n192.168.46.0/30\n192.168.45.0/24 metric 545\n', - 'route6': 'a:b:c:f::/64\na:b:c:d::/64\n', - 'rule': None, - 'rule6': None, - }, + "keys": None, + "route": "192.168.40.0/24 metric 545\n192.168.46.0/30\n" + "192.168.45.0/24 metric 545\n", + "route6": "a:b:c:f::/64\na:b:c:d::/64\n", + "rule": None, + "rule6": None, + } ], ) - self.do_connections_check_invalid([ { 'name': 'b', 'type': 'ethernet', 'mac': 'aa:b' } ]) + self.do_connections_check_invalid( + [{"name": "b", "type": "ethernet", "mac": "aa:b"}] + ) -@my_test_skipIf(nmutil is None, 'no support for NM (libnm via pygobject)') + +@my_test_skipIf(nmutil is None, "no support for NM (libnm via pygobject)") class TestNM(unittest.TestCase): - def test_connection_ensure_setting(self): con = NM.SimpleConnection.new() self.assertIsNotNone(con) @@ -1957,9 +1715,8 @@ class TestSysUtils(unittest.TestCase): # Manipulate PATH to use ethtool mock script to avoid hard dependency on # ethtool os.environ["PATH"] = TESTS_BASEDIR + "/helpers:" + os.environ["PATH"] - self.assertEqual(SysUtil._link_read_permaddress("lo"), - "23:00:00:00:00:00") + self.assertEqual(SysUtil._link_read_permaddress("lo"), "23:00:00:00:00:00") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tox.ini b/tox.ini index 28ba9ca..dbe1873 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = flake8, pylint, py{26,27,36,37} +envlist = black, flake8, pylint, py{26,27,36,37} skipsdist = true skip_missing_interpreters = True @@ -17,8 +17,16 @@ setenv = LC_ALL = C changedir = {toxinidir}/tests covtarget = network_connections +scriptfiles = library/network_connections.py tests/test_network_connections.py pytesttarget = . +[testenv:black] +basepython = python3.6 +deps = black + +commands = black --check --diff \ + {[base]scriptfiles} + [testenv:py26] passenv = {[base]passenv} setenv = @@ -76,10 +84,12 @@ commands = {[base]pytesttarget} [testenv:pylint] +basepython = python2.7 setenv = {[base]setenv} deps = pylint>=1.8.4 + ansible commands = pylint \ --errors-only \ @@ -108,10 +118,19 @@ addopts = -rxs [flake8] show_source = True +max-line-length = 88 +ignore = E402,W503 + +[pylint] +max-line-length = 88 +disable = wrong-import-position + +[pycodestyle] +max-line-length = 88 [travis] python = 2.6: py26 - 2.7: py27,coveralls - 3.6: py36 + 2.7: py27,coveralls,flake8,pylint + 3.6: py36,black 3.7: py37