mirror of
https://github.com/linux-system-roles/network.git
synced 2026-07-22 10:37:52 +00:00
Merge pull request #75 from tyll/interface_name
Use profile name for interface name by default
This commit is contained in:
commit
22539bdc18
3 changed files with 151 additions and 42 deletions
53
README.md
53
README.md
|
|
@ -12,31 +12,35 @@ The role can be used to configure:
|
|||
- Bonded interfaces
|
||||
- VLAN interfaces
|
||||
- MacVLAN interfaces
|
||||
- Infiniband interfaces
|
||||
- IP configuration
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
The role supports two providers: `nm` and `initscripts`. The provider can be configured per host
|
||||
via the [`provider`](#provider) variable. In absence of explicit configuration, it is autodetected based on
|
||||
the distribution. The `nm` provider is used by default on RHEL7 and
|
||||
`initscripts` on RHEL6. However, note that the provider is not tied to a certain distribution,
|
||||
given that the required API is available. For `nm` this means that at least version 1.2 of NetworkManager's
|
||||
API is available. For `initscripts`, it requires the legacy network service as commonly available
|
||||
on Fedora/RHEL.
|
||||
The role supports two providers: `nm` and `initscripts`. The provider can be
|
||||
configured per host via the [`network_provider`](#provider) variable. In
|
||||
absence of explicit configuration, it is autodetected based on the
|
||||
distribution. The `nm` provider is used by default on RHEL7 and `initscripts`
|
||||
on RHEL6. However, note that the provider is not tied to a certain
|
||||
distribution, given that the required API is available. For `nm` this means
|
||||
that at least version 1.2 of NetworkManager's API is available. For
|
||||
`initscripts`, it requires the legacy network service as commonly available on
|
||||
Fedora/RHEL.
|
||||
|
||||
For each host a list of networking profiles can be configure via the `network` variable.
|
||||
For each host a list of networking profiles can be configure via the
|
||||
`network_connections` variable.
|
||||
|
||||
- For initscripts, profiles correspond to ifcfg files in `/etc/sysconfig/network-scripts/ifcfg-*`.
|
||||
|
||||
- For NetworkManager, profiles correspond to connection profiles as handled by NetworkManager. Fedora and RHEL use the `rh-plugin` for NetworkManager which also writes configuration files to `/etc/sysconfig/network-scripts/ifcfg-*` for compatibility.
|
||||
|
||||
Note that the role primarily operates on networking profiles (connections) and
|
||||
not on devices. For example, in the role you would not configure the current IP address
|
||||
of an interface. Instead, you create a profile with a certain IP configuration and
|
||||
optionally activate the profile on a device. Which means, to apply the configuration
|
||||
to the actual networking interface.
|
||||
|
||||
not on devices but it defaults to use the profile name as the interface name.
|
||||
But it is also possible to create generic profiles, by creating for example a
|
||||
profile with a certain IP configuration without activating the profile. To
|
||||
apply the configuration to the actual networking interface, a command like
|
||||
`nmcli` needs to be used on the target system.
|
||||
|
||||
|
||||
Variables
|
||||
|
|
@ -141,9 +145,10 @@ By default, profiles are created with autoconnect enabled.
|
|||
|
||||
### `mac`
|
||||
|
||||
The `mac` address is optional and restricts the profile to be usable only
|
||||
on devices with the given MAC address. `mac` only makes sense for `type` `ethernet`
|
||||
to match a non-virtual device with the profile.
|
||||
The `mac` address is optional and restricts the profile to be usable only on
|
||||
devices with the given MAC address. `mac` is only allowed for `type`
|
||||
`ethernet` or `type` `infiniband` to match a non-virtual device with the
|
||||
profile.
|
||||
|
||||
- For NetworkManager `mac` is the permanent MAC address `ethernet.mac-address`.
|
||||
|
||||
|
|
@ -151,18 +156,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` and `infiniband`, 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`
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue