Use profile name for interface name by default

* by default ```name``` is used as ```interface_name```
* if ```interface_name``` is set to the empty string, it will not be set
* if ```mac``` is specified, ```interface_name``` defaults to not being set

This fixes #41
This commit is contained in:
Till Maas 2018-07-25 12:13:50 +02:00
parent 64879e74d9
commit 6bf70bcf60
3 changed files with 130 additions and 26 deletions

View file

@ -151,18 +151,22 @@ to match a non-virtual device with the profile.
### `interface_name`
For type `ethernet`, this option restricts the profile to the
given interface by name. This argument is optional and by default
a profile is not restricted to any interface by name.
Note that with [persistent interface naming](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Networking_Guide/ch-Consistent_Network_Device_Naming.html),
For type `ethernet`, this option restricts the profile to the given interface
by name. This argument is optional and by default the profile name is used
unless a mac address is specified using the `mac` key. Specifying an empty
string (`""`) allows to specify that the profile is not restricted to a network
interface.
**Note:** With [persistent interface naming](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Networking_Guide/ch-Consistent_Network_Device_Naming.html),
the interface is predictable based on the hardware configuration.
Otherwise, the `mac` address might be an option.
For virtual interface types like bridges, this argument is the name of the created
interface. In case of a missing `interface_name`, the profile name `name` is used.
Note the destinction between the profile name `name` and the device
name `interface_name`, which may or may not be the same.
**Note:** The profile name `name` and the device name `interface_name` may be
different or the profile may not be tied to an interface at all.
### `zone`

View file

@ -1080,7 +1080,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
enum_values=ArgValidator_DictConnection.VALID_SLAVE_TYPES,
),
ArgValidatorStr("master"),
ArgValidatorStr("interface_name"),
ArgValidatorStr("interface_name", allow_empty=True),
ArgValidatorMac("mac"),
ArgValidatorNum(
"mtu", val_min=0, val_max=0xFFFFFFFF, default_value=None
@ -1291,21 +1291,36 @@ class ArgValidator_DictConnection(ArgValidatorDict):
)
if "interface_name" in result:
if not Util.ifname_valid(result["interface_name"]):
# Ignore empty interface_name
if result["interface_name"] == "":
del result["interface_name"]
elif 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 result.get("mac"):
if not Util.ifname_valid(result["name"]):
raise ValidationError(
name + ".interface_name",
'requires \'interface_name\' as "name" "%s" is not valid'
'\'interface_name\' as "name" "%s" is not valid'
% (result["name"]),
)
result["interface_name"] = result["name"]
if "interface_name" not in result and result["type"] in [
"bond",
"bridge",
"macvlan",
"team",
"vlan",
]:
raise ValidationError(
name + ".interface_name",
"type '%s' requires 'interface_name'" % (result["type"]),
)
if result["type"] == "vlan":
if "vlan" not in result:
if "vlan_id" not in result:

View file

@ -2,11 +2,12 @@
""" Tests for network_connections Ansible module """
# SPDX-License-Identifier: BSD-3-Clause
import sys
import os
import unittest
import socket
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"))
@ -43,9 +44,8 @@ if nmutil:
def pprint(msg, obj):
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()
@ -281,7 +281,7 @@ class TestValidator(unittest.TestCase):
"zone": None,
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "5",
"check_iface_exists": True,
"slave_type": None,
},
@ -324,7 +324,7 @@ class TestValidator(unittest.TestCase):
"zone": None,
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "5",
"check_iface_exists": True,
"force_state_change": None,
"slave_type": None,
@ -362,7 +362,7 @@ class TestValidator(unittest.TestCase):
"zone": None,
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "5",
"check_iface_exists": True,
"force_state_change": None,
"slave_type": None,
@ -379,6 +379,7 @@ class TestValidator(unittest.TestCase):
"NM_CONTROLLED": "no",
"ONBOOT": "no",
"TYPE": "Ethernet",
"DEVICE": "5",
},
"keys": None,
"route": None,
@ -488,7 +489,7 @@ class TestValidator(unittest.TestCase):
"master": None,
"mtu": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "prod1",
"type": "ethernet",
"slave_type": None,
"wait": None,
@ -1185,7 +1186,7 @@ class TestValidator(unittest.TestCase):
"zone": None,
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "5",
"check_iface_exists": True,
"force_state_change": None,
"slave_type": None,
@ -1224,7 +1225,7 @@ class TestValidator(unittest.TestCase):
"zone": None,
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "5",
"check_iface_exists": True,
"force_state_change": None,
"slave_type": None,
@ -1250,6 +1251,7 @@ class TestValidator(unittest.TestCase):
"NM_CONTROLLED": "no",
"ONBOOT": "yes",
"TYPE": "Ethernet",
"DEVICE": "5",
},
"keys": None,
"route": None,
@ -1301,7 +1303,7 @@ class TestValidator(unittest.TestCase):
"ethernet": {"autoneg": None, "duplex": None, "speed": 0},
"force_state_change": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "6643",
"ip": {
"address": [],
"auto6": True,
@ -1376,7 +1378,14 @@ class TestValidator(unittest.TestCase):
"zone": None,
}
],
[{"name": "infiniband.1", "state": "up", "type": "infiniband"}],
[
{
"name": "infiniband.1",
"interface_name": "",
"state": "up",
"type": "infiniband",
}
],
initscripts_dict_expected=[
{
"ifcfg": {
@ -1512,7 +1521,7 @@ class TestValidator(unittest.TestCase):
"zone": None,
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "555",
"check_iface_exists": True,
"force_state_change": None,
"slave_type": None,
@ -1543,6 +1552,7 @@ class TestValidator(unittest.TestCase):
"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",
@ -1604,7 +1614,7 @@ class TestValidator(unittest.TestCase):
"zone": "external",
"master": None,
"ignore_errors": None,
"interface_name": None,
"interface_name": "e556",
"check_iface_exists": True,
"force_state_change": None,
"slave_type": None,
@ -1666,6 +1676,7 @@ class TestValidator(unittest.TestCase):
"ONBOOT": "yes",
"TYPE": "Ethernet",
"ZONE": "external",
"DEVICE": "e556",
},
"keys": None,
"route": "192.168.40.0/24 metric 545\n192.168.46.0/30\n"
@ -1681,6 +1692,80 @@ class TestValidator(unittest.TestCase):
[{"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 inerface_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.assertEquals(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.assertEquals(connections[0]["interface_name"], "internal")
@my_test_skipIf(nmutil is None, "no support for NM (libnm via pygobject)")
class TestNM(unittest.TestCase):