#!/usr/bin/env python """ Tests for network_connections Ansible module """ # SPDX-License-Identifier: BSD-3-Clause import itertools import os import pprint as pprint_ import socket import sys import unittest TESTS_BASEDIR = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(1, os.path.join(TESTS_BASEDIR, "../..", "library")) sys.path.insert(1, os.path.join(TESTS_BASEDIR, "../..", "module_utils")) try: from unittest import mock except ImportError: # py2 import mock sys.modules["ansible"] = mock.Mock() sys.modules["ansible.module_utils.basic"] = mock.Mock() sys.modules["ansible.module_utils"] = mock.Mock() sys.modules["ansible.module_utils.network_lsr"] = __import__("network_lsr") # pylint: disable=import-error, wrong-import-position import network_lsr import network_connections as n from network_connections import SysUtil from network_connections import Util try: my_test_skipIf = unittest.skipIf except AttributeError: # python 2.6 workaround def my_test_skipIf(condition, reason): if condition: return lambda x: None else: return lambda x: x try: nmutil = n.NMUtil() assert nmutil except Exception: # NMUtil is not supported, for example on RHEL 6 or without # pygobject. nmutil = None if nmutil: NM = n.Util.NM() GObject = n.Util.GObject() def pprint(msg, obj): print("PRINT: %s\n" % (msg)) p = pprint_.PrettyPrinter(indent=4) p.pprint(obj) if nmutil is not None and isinstance(obj, NM.Connection): obj.dump() ARGS_CONNECTIONS = network_lsr.argument_validator.ArgValidator_ListConnections() VALIDATE_ONE_MODE_INITSCRIPTS = ARGS_CONNECTIONS.VALIDATE_ONE_MODE_INITSCRIPTS ETHTOOL_FEATURES_DEFAULTS = { "esp-hw-offload": None, "esp-tx-csum-hw-offload": None, "fcoe-mtu": None, "gro": None, "gso": None, "highdma": None, "hw-tc-offload": None, "l2-fwd-offload": None, "loopback": None, "lro": None, "ntuple": None, "rx": None, "rx-all": None, "rx-fcs": None, "rx-gro-hw": None, "rx-udp_tunnel-port-offload": None, "rx-vlan-filter": None, "rx-vlan-stag-filter": None, "rx-vlan-stag-hw-parse": None, "rxhash": None, "rxvlan": None, "sg": None, "tls-hw-record": None, "tls-hw-tx-offload": None, "tso": None, "tx": None, "tx-checksum-fcoe-crc": None, "tx-checksum-ip-generic": None, "tx-checksum-ipv4": None, "tx-checksum-ipv6": None, "tx-checksum-sctp": None, "tx-esp-segmentation": None, "tx-fcoe-segmentation": None, "tx-gre-csum-segmentation": None, "tx-gre-segmentation": None, "tx-gso-partial": None, "tx-gso-robust": None, "tx-ipxip4-segmentation": None, "tx-ipxip6-segmentation": None, "tx-nocache-copy": None, "tx-scatter-gather": None, "tx-scatter-gather-fraglist": None, "tx-sctp-segmentation": None, "tx-tcp-ecn-segmentation": None, "tx-tcp-mangleid-segmentation": None, "tx-tcp-segmentation": None, "tx-tcp6-segmentation": None, "tx-udp-segmentation": None, "tx-udp_tnl-csum-segmentation": None, "tx-udp_tnl-segmentation": None, "tx-vlan-stag-hw-insert": None, "txvlan": None, } ETHTOOL_DEFAULTS = {"features": ETHTOOL_FEATURES_DEFAULTS} ETHERNET_DEFAULTS = {"autoneg": None, "duplex": None, "speed": 0} class TestValidator(unittest.TestCase): def setUp(self): # default values when "type" is specified and state is not self.default_connection_settings = { "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "ignore_errors": 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": [], }, "mac": None, "master": None, "mtu": None, "name": "5", "parent": None, "slave_type": None, "zone": None, } def assertValidationError(self, v, value): self.assertRaises(n.ValidationError, v.validate, value) def assert_nm_connection_routes_expected(self, connection, route_list_expected): parser = network_lsr.argument_validator.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 ] self.assertEqual(route_list_exp, route_list_new) def do_connections_check_invalid(self, input_connections): self.assertValidationError(ARGS_CONNECTIONS, input_connections) def do_connections_validate_nm(self, input_connections, **kwargs): if not nmutil: 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() mode = VALIDATE_ONE_MODE_INITSCRIPTS for idx, connection in enumerate(connections): try: ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx) except n.ValidationError: continue 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 = network_lsr.argument_validator.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]: r = parser.validate(r) 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 ) 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] ) def do_connections_validate_ifcfg(self, input_connections, **kwargs): mode = VALIDATE_ONE_MODE_INITSCRIPTS connections = ARGS_CONNECTIONS.validate(input_connections) for idx, connection in enumerate(connections): try: ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx) except n.ValidationError: continue if "type" not in connection: continue if connection["type"] in ["macvlan"]: # initscripts do not support this type. Skip the test. continue 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) if exp is not None: self.assertEqual(exp[idx], c) 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) self.do_connections_validate_ifcfg(input_connections, **kwargs) def test_validate_str(self): v = network_lsr.argument_validator.ArgValidatorStr("state") self.assertEqual("a", v.validate("a")) self.assertValidationError(v, 1) self.assertValidationError(v, None) v = network_lsr.argument_validator.ArgValidatorStr("state", required=True) self.assertValidationError(v, None) def test_validate_int(self): v = network_lsr.argument_validator.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 = network_lsr.argument_validator.ArgValidatorNum("state", default_value=None) self.assertEqual(1, v.validate(1)) self.assertEqual(1, v.validate(1.0)) self.assertEqual(1, v.validate("1")) self.assertValidationError(v, None) self.assertValidationError(v, None) self.assertValidationError(v, 1.5) self.assertValidationError(v, "1.5") v = network_lsr.argument_validator.ArgValidatorNum("state", required=True) self.assertValidationError(v, None) def test_validate_bool(self): v = network_lsr.argument_validator.ArgValidatorBool("state") self.assertEqual(True, v.validate("yes")) self.assertEqual(True, v.validate("yeS")) self.assertEqual(True, v.validate("Y")) self.assertEqual(True, v.validate(True)) self.assertEqual(True, v.validate("True")) self.assertEqual(True, v.validate("1")) self.assertEqual(True, v.validate(1)) self.assertEqual(False, v.validate("no")) self.assertEqual(False, v.validate("nO")) self.assertEqual(False, v.validate("N")) self.assertEqual(False, v.validate(False)) self.assertEqual(False, v.validate("False")) self.assertEqual(False, v.validate("0")) self.assertEqual(False, v.validate(0)) self.assertValidationError(v, 2) self.assertValidationError(v, -1) self.assertValidationError(v, "Ye") self.assertValidationError(v, "") self.assertValidationError(v, None) v = network_lsr.argument_validator.ArgValidatorBool("state", required=True) self.assertValidationError(v, None) def test_validate_dict(self): v = network_lsr.argument_validator.ArgValidatorDict( "dict", nested=[ network_lsr.argument_validator.ArgValidatorNum("i", required=True), network_lsr.argument_validator.ArgValidatorStr( "s", required=False, default_value="s_default" ), network_lsr.argument_validator.ArgValidatorStr( "l", required=False, default_value=network_lsr.argument_validator.ArgValidator.MISSING, ), ], ) self.assertEqual({"i": 5, "s": "s_default"}, v.validate({"i": "5"})) self.assertEqual( {"i": 5, "s": "s_default", "l": "6"}, v.validate({"i": "5", "l": "6"}) ) self.assertValidationError(v, {"k": 1}) def test_validate_list(self): v = network_lsr.argument_validator.ArgValidatorList( "list", nested=network_lsr.argument_validator.ArgValidatorNum("i") ) self.assertEqual([1, 5], v.validate(["1", 5])) self.assertValidationError(v, [1, "s"]) def test_empty(self): self.maxDiff = None self.do_connections_validate([], []) def test_ethernet_two_defaults(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "ignore_errors": None, "interface_name": "5", "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": [], }, "mac": None, "master": None, "mtu": None, "name": "5", "parent": None, "persistent_state": "present", "slave_type": None, "state": None, "type": "ethernet", "zone": None, }, { "actions": ["present"], "ignore_errors": None, "name": "5", "persistent_state": "present", "state": None, }, ], [{"name": "5", "type": "ethernet"}, {"name": "5"}], ) def test_up_ethernet(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "5", "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, }, "mac": None, "master": None, "mtu": None, "name": "5", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": None, } ], [{"name": "5", "state": "up", "type": "ethernet"}], ) def test_up_ethernet_no_autoconnect(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": False, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "5", "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, }, "mac": None, "master": None, "mtu": None, "name": "5", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": 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", "DEVICE": "5", }, "keys": None, "route": None, "route6": None, "rule": None, "rule6": None, } ], ) def test_invalid_autoconnect(self): self.maxDiff = None self.do_connections_check_invalid([{"name": "a", "autoconnect": True}]) def test_absent(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["absent"], "ignore_errors": None, "name": "5", "persistent_state": "absent", "state": None, } ], [{"name": "5", "persistent_state": "absent"}], ) def test_up_ethernet_mac_mtu_static_ip(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": 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", } ], "route_append_only": False, "rule_append_only": False, "route": [], }, "mac": "52:54:00:44:9f:ba", "master": None, "mtu": 1450, "name": "prod1", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": 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"}, } ], ) def test_up_single_v4_dns(self): self.maxDiff = None # set single IPv4 DNS server self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "prod1", "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", } ], "route_append_only": False, "rule_append_only": False, "route": [], }, "mac": None, "master": None, "mtu": None, "name": "prod1", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": None, } ], [ { "name": "prod1", "state": "up", "type": "ethernet", "autoconnect": "yes", "ip": {"address": "192.168.174.5/24", "dns": "192.168.174.1"}, } ], ) def test_routes(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": 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.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": [], }, "mac": "52:54:00:44:9f:ba", "master": None, "mtu": 1450, "name": "prod1", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": None, }, { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "prod.100", "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, } ], }, "mac": None, "master": None, "mtu": None, "name": "prod.100", "parent": "prod1", "persistent_state": "present", "slave_type": None, "state": "up", "type": "vlan", "vlan": {"id": 100}, "wait": None, "zone": 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": "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"}], }, }, ], ) def test_vlan(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": 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.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": [], }, "mac": "52:54:00:44:9f:ba", "master": None, "mtu": 1450, "name": "prod1", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": None, }, { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "prod.100", "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, } ], }, "mac": None, "master": None, "mtu": None, "name": "prod.100", "parent": "prod1", "persistent_state": "present", "slave_type": None, "state": "up", "type": "vlan", "vlan": {"id": 101}, "wait": None, "zone": 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": "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"}], }, }, ], ) def test_macvlan(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "eth0", "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": [], }, "mac": "33:24:10:24:2f:b9", "master": None, "mtu": 1450, "name": "eth0-parent", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": None, }, { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "veth0", "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, } ], }, "mac": None, "macvlan": {"mode": "bridge", "promiscuous": True, "tap": False}, "master": None, "mtu": None, "name": "veth0.0", "parent": "eth0-parent", "persistent_state": "present", "slave_type": None, "state": "up", "type": "macvlan", "wait": None, "zone": None, }, { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "veth1", "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, "macvlan": {"mode": "passthru", "promiscuous": False, "tap": True}, "master": None, "mtu": None, "name": "veth0.1", "parent": "eth0-parent", "persistent_state": "present", "slave_type": None, "state": "up", "type": "macvlan", "wait": None, "zone": 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"}], }, }, { "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"}], }, }, ], ) def test_bridge_no_dhcp4_auto6(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "bridge2", "ip": { "address": [], "auto6": False, "dhcp4": False, "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": "prod2", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "bridge", "wait": None, "zone": None, }, { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "eth1", "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": "prod2", "mtu": None, "name": "prod2-slave1", "parent": None, "persistent_state": "present", "slave_type": "bridge", "state": "up", "type": "ethernet", "wait": None, "zone": 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", }, ], ) def test_bond(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "bond": {"mode": "balance-rr", "miimon": None}, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "bond1", "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": [], }, "mac": None, "master": None, "mtu": None, "name": "bond1", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "bond", "wait": None, "zone": None, } ], [{"name": "bond1", "state": "up", "type": "bond"}], ) def test_bond_active_backup(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "bond": {"mode": "active-backup", "miimon": None}, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "bond1", "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": [], }, "mac": None, "master": None, "mtu": None, "name": "bond1", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "bond", "wait": None, "zone": None, } ], [ { "name": "bond1", "state": "up", "type": "bond", "bond": {"mode": "active-backup"}, } ], ) def test_invalid_values(self): self.maxDiff = None self.do_connections_check_invalid([{}]) self.do_connections_check_invalid([{"name": "b", "xxx": 5}]) def test_ethernet_mac_address(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "ignore_errors": None, "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": [], }, "mac": "aa:bb:cc:dd:ee:ff", "master": None, "mtu": None, "name": "5", "parent": None, "persistent_state": "present", "slave_type": None, "state": None, "type": "ethernet", "zone": None, } ], [{"name": "5", "type": "ethernet", "mac": "AA:bb:cC:DD:ee:FF"}], ) def test_ethernet_speed_settings(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": {"autoneg": False, "duplex": "half", "speed": 400}, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "5", "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, }, "mac": None, "master": None, "mtu": None, "name": "5", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": 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", "DEVICE": "5", }, "keys": None, "route": None, "route6": None, "rule": None, "rule6": None, } ], ) def test_bridge2(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "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, "persistent_state": "present", "slave_type": None, "state": "up", "type": "bridge", "wait": None, "zone": None, }, { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "6643", "ip": { "address": [], "auto6": True, "dhcp4_send_hostname": None, "dhcp4": True, "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, "persistent_state": "present", "slave_type": "bridge", "state": "up", "type": "ethernet", "wait": None, "zone": None, }, ], [ {"name": "6643-master", "state": "up", "type": "bridge"}, { "name": "6643", "state": "up", "type": "ethernet", "master": "6643-master", }, ], ) def test_infiniband(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethtool": ETHTOOL_DEFAULTS, "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, "persistent_state": "present", "slave_type": None, "state": "up", "type": "infiniband", "wait": None, "zone": None, } ], [ { "name": "infiniband.1", "interface_name": "", "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, } ], ) def test_infiniband2(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethtool": ETHTOOL_DEFAULTS, "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, "persistent_state": "present", "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, } ], ) def test_route_metric_prefix(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "555", "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, }, "mac": None, "master": None, "mtu": None, "name": "555", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": 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", "DEVICE": "555", }, "keys": None, "route": "192.168.45.0/24 metric 545\n192.168.46.0/30\n", "route6": None, "rule": None, "rule6": None, } ], ) def test_route_v6(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present", "up"], "autoconnect": True, "check_iface_exists": True, "ethernet": ETHERNET_DEFAULTS, "ethtool": ETHTOOL_DEFAULTS, "force_state_change": None, "ignore_errors": None, "interface_name": "e556", "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, }, ], "dns": [], "dns_search": ["aa", "bb"], "route_metric6": None, "dhcp4_send_hostname": None, }, "mac": None, "master": None, "mtu": None, "name": "e556", "parent": None, "persistent_state": "present", "slave_type": None, "state": "up", "type": "ethernet", "wait": None, "zone": "external", } ], [ { "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=[ [ {"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=[ [ {"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=[ { "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=[ { "ifcfg": { "BOOTPROTO": "dhcp", "DOMAIN": "aa bb", "IPV6INIT": "yes", "IPV6_AUTOCONF": "yes", "NM_CONTROLLED": "no", "ONBOOT": "yes", "TYPE": "Ethernet", "ZONE": "external", "DEVICE": "e556", }, "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, } ], ) def test_invalid_mac(self): self.maxDiff = None self.do_connections_check_invalid( [{"name": "b", "type": "ethernet", "mac": "aa:b"}] ) def test_interface_name_ethernet_default(self): """ Use the profile name as interface_name for ethernet profiles """ cons_without_interface_name = [{"name": "eth0", "type": "ethernet"}] connections = ARGS_CONNECTIONS.validate(cons_without_interface_name) self.assertTrue(connections[0]["interface_name"] == "eth0") def test_interface_name_ethernet_mac(self): """ Do not set interface_name when mac is specified """ cons_without_interface_name = [ {"name": "eth0", "type": "ethernet", "mac": "3b:0b:88:16:6d:1a"} ] connections = ARGS_CONNECTIONS.validate(cons_without_interface_name) self.assertTrue(connections[0]["interface_name"] is None) def test_interface_name_ethernet_empty(self): """ Allow not to restrict the profile to an interface """ network_connections = [ {"name": "internal_network", "type": "ethernet", "interface_name": ""} ] connections = ARGS_CONNECTIONS.validate(network_connections) self.assertTrue(connections[0]["interface_name"] is None) def test_interface_name_ethernet_None(self): """ Check that interface_name cannot be None """ network_connections = [ {"name": "internal_network", "type": "ethernet", "interface_name": None} ] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) def test_interface_name_ethernet_explicit(self): """ Use the explicitly provided interface name """ network_connections = [ {"name": "internal", "type": "ethernet", "interface_name": "eth0"} ] connections = ARGS_CONNECTIONS.validate(network_connections) self.assertEqual(connections[0]["interface_name"], "eth0") def test_interface_name_ethernet_invalid_profile(self): """ Require explicit interface_name when the profile name is not a valid interface_name """ network_connections = [{"name": "internal:main", "type": "ethernet"}] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) network_connections = [ {"name": "internal:main", "type": "ethernet", "interface_name": "eth0"} ] connections = ARGS_CONNECTIONS.validate(network_connections) self.assertTrue(connections[0]["interface_name"] == "eth0") def test_interface_name_ethernet_invalid_interface_name(self): network_connections = [ {"name": "internal", "type": "ethernet", "interface_name": "invalid:name"} ] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) def test_interface_name_bond_empty_interface_name(self): network_connections = [ {"name": "internal", "type": "bond", "interface_name": "invalid:name"} ] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) def test_interface_name_bond_profile_as_interface_name(self): network_connections = [{"name": "internal", "type": "bond"}] connections = ARGS_CONNECTIONS.validate(network_connections) self.assertEqual(connections[0]["interface_name"], "internal") def check_connection(self, connection, expected): reduced_connection = {} for setting in expected: reduced_connection[setting] = connection[setting] self.assertEqual(reduced_connection, expected) def check_partial_connection_zero(self, network_config, expected): connections = ARGS_CONNECTIONS.validate([network_config]) self.check_connection(connections[0], expected) def check_one_connection_with_defaults( self, network_config, expected_changed_settings ): self.maxDiff = None expected = self.default_connection_settings expected.update(expected_changed_settings) self.do_connections_validate([expected], [network_config]) def test_default_states(self): self.check_partial_connection_zero( {"name": "eth0"}, {"actions": ["present"], "persistent_state": "present", "state": None}, ) def test_invalid_persistent_state_up(self): network_connections = [{"name": "internal", "persistent_state": "up"}] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) def test_invalid_persistent_state_down(self): network_connections = [{"name": "internal", "persistent_state": "down"}] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) def test_invalid_state_test(self): network_connections = [{"name": "internal", "state": "test"}] self.assertRaises( n.ValidationError, ARGS_CONNECTIONS.validate, network_connections ) def test_default_states_type(self): self.check_partial_connection_zero( {"name": "eth0", "type": "ethernet"}, {"actions": ["present"], "persistent_state": "present", "state": None}, ) def test_persistent_state_present(self): self.check_partial_connection_zero( {"name": "eth0", "persistent_state": "present", "type": "ethernet"}, {"actions": ["present"], "persistent_state": "present", "state": None}, ) def test_state_present(self): self.check_partial_connection_zero( {"name": "eth0", "state": "present", "type": "ethernet"}, {"actions": ["present"], "persistent_state": "present", "state": None}, ) def test_state_absent(self): self.check_partial_connection_zero( {"name": "eth0", "state": "absent"}, {"actions": ["absent"], "persistent_state": "absent", "state": None}, ) def test_persistent_state_absent(self): self.check_partial_connection_zero( {"name": "eth0", "persistent_state": "absent"}, {"actions": ["absent"], "persistent_state": "absent", "state": None}, ) def test_state_present_up(self): self.check_partial_connection_zero( { "name": "eth0", "persistent_state": "present", "state": "up", "type": "ethernet", }, { "actions": ["present", "up"], "persistent_state": "present", "state": "up", }, ) def test_state_present_down(self): self.check_partial_connection_zero( { "name": "eth0", "persistent_state": "present", "state": "down", "type": "ethernet", }, { "actions": ["present", "down"], "persistent_state": "present", "state": "down", }, ) def test_state_absent_up_no_type(self): self.check_partial_connection_zero( {"name": "eth0", "persistent_state": "absent", "state": "up"}, { "actions": ["present", "up", "absent"], "persistent_state": "absent", "state": "up", }, ) def test_state_absent_up_type(self): # if type is specified, present should happen, too self.check_partial_connection_zero( { "name": "eth0", "persistent_state": "absent", "state": "up", "type": "ethernet", }, { "actions": ["present", "up", "absent"], "persistent_state": "absent", "state": "up", }, ) def test_state_absent_down(self): # if type is specified, present should happen, too self.check_partial_connection_zero( {"name": "eth0", "persistent_state": "absent", "state": "down"}, { "actions": ["present", "down", "absent"], "persistent_state": "absent", "state": "down", }, ) def test_state_up_no_type(self): self.check_partial_connection_zero( {"name": "eth0", "state": "up"}, { "actions": ["present", "up"], "persistent_state": "present", "state": "up", }, ) def test_state_up_type(self): self.check_partial_connection_zero( {"name": "eth0", "state": "up", "type": "ethernet"}, { "actions": ["present", "up"], "persistent_state": "present", "state": "up", }, ) def test_state_down_no_type(self): self.check_partial_connection_zero( {"name": "eth0", "state": "down"}, { "actions": ["present", "down"], "persistent_state": "present", "state": "down", }, ) def test_full_state_present_no_type(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["present"], "ignore_errors": None, "name": "eth0", "state": None, "persistent_state": "present", } ], [{"name": "eth0", "persistent_state": "present"}], ) def test_full_state_present_type_defaults(self): self.check_one_connection_with_defaults( {"name": "eth0", "type": "ethernet", "persistent_state": "present"}, { "actions": ["present"], "interface_name": "eth0", "name": "eth0", "persistent_state": "present", "state": None, "type": "ethernet", }, ) def test_full_state_absent_no_type(self): self.maxDiff = None self.do_connections_validate( [ { "actions": ["absent"], "ignore_errors": None, "name": "eth0", "state": None, "persistent_state": "absent", } ], [{"name": "eth0", "persistent_state": "absent"}], ) def test_full_state_absent_defaults(self): self.maxDiff = None self.check_one_connection_with_defaults( {"name": "eth0", "persistent_state": "absent", "type": "ethernet"}, { "actions": ["absent"], "ignore_errors": None, "name": "eth0", "state": None, "persistent_state": "absent", "type": "ethernet", "interface_name": "eth0", }, ) @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) self.assertTrue(GObject.type_is_a(con, NM.Connection)) s = nmutil.connection_ensure_setting(con, NM.SettingWired) self.assertIsNotNone(s) self.assertTrue(GObject.type_is_a(s, NM.SettingWired)) s2 = nmutil.connection_ensure_setting(con, NM.SettingWired) self.assertIsNotNone(s2) self.assertIs(s, s2) self.assertTrue(GObject.type_is_a(s, NM.SettingWired)) def test_connection_list(self): connections = nmutil.connection_list() self.assertIsNotNone(connections) class TestUtils(unittest.TestCase): def test_check_output(self): res = Util.check_output(["echo", "test"]) self.assertEqual(res, "test\n") self.assertRaises(n.MyError, Util.check_output, ["false"]) class TestSysUtils(unittest.TestCase): def test_link_read_permaddress(self): # 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") if __name__ == "__main__": unittest.main()