mirror of
https://github.com/linux-system-roles/network.git
synced 2026-07-21 02:11:23 +00:00
library: merge branch 'macvlan-pr27'
https://github.com/linux-system-roles/network/pull/27
This commit is contained in:
commit
84e4d3ac95
4 changed files with 336 additions and 13 deletions
29
README.md
29
README.md
|
|
@ -8,6 +8,7 @@ The role can be used to configure:
|
|||
- Bridge interfaces
|
||||
- Bonded interfaces
|
||||
- VLAN interfaces
|
||||
- MacVLAN interfaces
|
||||
- IP configuration
|
||||
|
||||
General
|
||||
|
|
@ -363,6 +364,34 @@ network_connections:
|
|||
Like for `master`, the `parent` references the connection profile in the ansible
|
||||
role.
|
||||
|
||||
### `type: macvlan`
|
||||
|
||||
MACVLANs also work:
|
||||
|
||||
```yaml
|
||||
network_connections:
|
||||
- name: eth0-profile
|
||||
type: ethernet
|
||||
interface_name: eth0
|
||||
ip:
|
||||
address:
|
||||
- 192.168.0.1/24
|
||||
|
||||
- name: veth0
|
||||
type: macvlan
|
||||
parent: eth0-profile
|
||||
macvlan:
|
||||
mode: bridge
|
||||
promiscuous: True
|
||||
tap: False
|
||||
ip:
|
||||
address:
|
||||
- 192.168.1.1/24
|
||||
```
|
||||
|
||||
Like for `master` and `vlan`, the `parent` references the connection profile in the ansible
|
||||
role.
|
||||
|
||||
### `network_provider`
|
||||
|
||||
Whether to use `nm` or `initscripts` is detected based on the distribution.
|
||||
|
|
|
|||
26
examples/macvlan.yml
Normal file
26
examples/macvlan.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
- hosts: network-test
|
||||
vars:
|
||||
network_connections:
|
||||
|
||||
- name: eth0
|
||||
type: ethernet
|
||||
interface_name: eth0
|
||||
ip:
|
||||
address:
|
||||
- 192.168.0.1/24
|
||||
|
||||
# Create a virtual ethernet card bound to eth0
|
||||
- name: veth0
|
||||
type: macvlan
|
||||
parent: eth0
|
||||
macvlan:
|
||||
mode: bridge
|
||||
promiscuous: True
|
||||
tap: False
|
||||
ip:
|
||||
address:
|
||||
- 192.168.1.1/24
|
||||
|
||||
roles:
|
||||
- network
|
||||
|
|
@ -851,11 +851,37 @@ class ArgValidator_DictVlan(ArgValidatorDict):
|
|||
'id': None,
|
||||
}
|
||||
|
||||
class ArgValidator_DictMacvlan(ArgValidatorDict):
|
||||
|
||||
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),
|
||||
],
|
||||
default_value = ArgValidator.MISSING,
|
||||
)
|
||||
|
||||
def get_default_macvlan(self):
|
||||
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')
|
||||
return result
|
||||
|
||||
class ArgValidator_DictConnection(ArgValidatorDict):
|
||||
|
||||
VALID_STATES = ['up', 'down', 'present', 'absent', 'wait']
|
||||
VALID_TYPES = [ 'ethernet', 'infiniband', 'bridge', 'team', 'bond', 'vlan' ]
|
||||
VALID_TYPES = [ 'ethernet', 'infiniband', 'bridge', 'team', 'bond', 'vlan', 'macvlan' ]
|
||||
VALID_SLAVE_TYPES = [ 'bridge', 'bond', 'team' ]
|
||||
|
||||
def __init__(self):
|
||||
|
|
@ -882,6 +908,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
ArgValidator_DictBond(),
|
||||
ArgValidator_DictInfiniband(),
|
||||
ArgValidator_DictVlan(),
|
||||
ArgValidator_DictMacvlan(),
|
||||
|
||||
# deprecated options:
|
||||
ArgValidatorStr ('infiniband_transport_mode', enum_values = ['datagram', 'connected'], default_value = ArgValidator.MISSING),
|
||||
|
|
@ -1003,7 +1030,7 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
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' ]:
|
||||
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']
|
||||
|
|
@ -1027,8 +1054,8 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
raise ValidationError(name + '.vlan_id', '"vlan_id" is only allowed for "type" "vlan"')
|
||||
|
||||
if 'parent' in result:
|
||||
if result['type'] not in ['vlan', 'infiniband']:
|
||||
raise ValidationError(name + '.parent', '"parent" is only allowed for type "vlan" or "infiniband"')
|
||||
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')
|
||||
|
||||
|
|
@ -1046,6 +1073,13 @@ class ArgValidator_DictConnection(ArgValidatorDict):
|
|||
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()
|
||||
else:
|
||||
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:
|
||||
continue
|
||||
|
|
@ -1667,6 +1701,18 @@ class NMUtil:
|
|||
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':
|
||||
# convert mode name to a number (which is actually expected by nm)
|
||||
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))
|
||||
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))
|
||||
else:
|
||||
raise MyError('unsupported type %s' % (connection['type']))
|
||||
|
||||
|
|
@ -2600,6 +2646,12 @@ class Cmd_initscripts(Cmd):
|
|||
Cmd.__init__(self, **kwargs)
|
||||
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['type'] in [ 'macvlan' ]:
|
||||
self.log_fatal(idx, 'unsupported type %s for initscripts provider' % (connection['type']))
|
||||
|
||||
def check_name(self, idx, name = None):
|
||||
if name is None:
|
||||
name = self.connections[idx]['name']
|
||||
|
|
|
|||
|
|
@ -112,15 +112,19 @@ class TestValidator(unittest.TestCase):
|
|||
ARGS_CONNECTIONS.validate_connection_one(mode, connections, idx)
|
||||
except n.ValidationError as e:
|
||||
continue
|
||||
if 'type' in connection:
|
||||
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)
|
||||
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):
|
||||
|
|
@ -705,6 +709,218 @@ 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': []
|
||||
},
|
||||
'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,
|
||||
},
|
||||
],
|
||||
},
|
||||
'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': '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',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
'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(
|
||||
[
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue