mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
nmcli: add idempotent support for any kinds of connections (#562)
* nmcli: add idemptent support for any kinds of connections Fixes #481: nmcli reports changed status even if nothing needs to change - Implement show_connection() to retrieve connection profile from command line - Parse integer enumeration values in show_connection() - Convert 'bond.options' to alias shortcuts - Modify connection only if changes are detected - Support generic alias in during the property comparison * nmcli: add idemptent support for any kinds of connections Add mock object for modification cases when connection state changes * nmcli: add idempotent support for any kinds of connections - Add more test cases to check idempotent for each type of connections - Verify 'changed' and 'failed' in the result of each test - Append prefixlen for 'ip4' values in test data - Fix the incorrect 'return_value' of execute_command() in previous mockers - Ignore the empty string in _compare_conn_params() - Fix the property key mapping of 'bridge-port.hairpin-mode' for bridge-slave - Add 'override_options' in the result output for playboot debug * nmcli: add idempotent support for any kinds of connections Fix pep8 issues in test_nmcli.py: Comparison to False should be 'not expr' * nmcli: add idempotent support for any kinds of connections Support setting 'ipv4.method' or 'ipv6.method' via nmcli if the configuration method changes * nmcli: add idempotent support for any kinds of connections Simplify the if statements in show_connection() according to vlours's advice * nmcli: add idempotent support for any kinds of connections Fix the list argument comparison method with multiple values. * nmcli: add idempotent support for any kinds of connections Use ansible --diff option output to show detailed changes instead of a private return value. * nmcli: add idempotent support for any kinds of connections Add changelog fragment for bugfix.
This commit is contained in:
parent
706195fb02
commit
d2ee51253d
3 changed files with 908 additions and 62 deletions
2
changelogs/fragments/562-nmcli-fix-idempotency.yaml
Normal file
2
changelogs/fragments/562-nmcli-fix-idempotency.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- nmcli - fix idempotetency when modifying an existing connection (https://github.com/ansible-collections/community.general/issues/481).
|
|
@ -568,6 +568,11 @@ except (ImportError, ValueError):
|
|||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
import re
|
||||
|
||||
|
||||
class NmcliModuleError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Nmcli(object):
|
||||
|
@ -670,6 +675,20 @@ class Nmcli(object):
|
|||
self.nmcli_bin = self.module.get_bin_path('nmcli', True)
|
||||
self.dhcp_client_id = module.params['dhcp_client_id']
|
||||
|
||||
if self.ip4:
|
||||
self.ipv4_method = 'manual'
|
||||
else:
|
||||
# supported values for 'ipv4.method': [auto, link-local, manual, shared, disabled]
|
||||
# TODO: add a new module parameter to specify a non 'manual' value
|
||||
self.ipv4_method = None
|
||||
|
||||
if self.ip6:
|
||||
self.ipv6_method = 'manual'
|
||||
else:
|
||||
# supported values for 'ipv6.method': [ignore, auto, dhcp, link-local, manual, shared]
|
||||
# TODO: add a new module parameter to specify a non 'manual' value
|
||||
self.ipv6_method = None
|
||||
|
||||
def execute_command(self, cmd, use_unsafe_shell=False, data=None):
|
||||
if isinstance(cmd, list):
|
||||
cmd = [to_text(item) for item in cmd]
|
||||
|
@ -816,9 +835,11 @@ class Nmcli(object):
|
|||
def modify_connection_team(self):
|
||||
cmd = [self.nmcli_bin, 'con', 'mod', self.conn_name]
|
||||
options = {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.address': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.address': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
|
@ -898,9 +919,11 @@ class Nmcli(object):
|
|||
# format for modifying bond interface
|
||||
|
||||
options = {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.address': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.address': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
|
@ -988,9 +1011,11 @@ class Nmcli(object):
|
|||
# - nmcli: conn_name=my-eth1 ifname=eth1 type=ethernet ip4=192.0.2.100/24 gw4=192.0.2.1 state=present
|
||||
# nmcli con mod con-name my-eth1 ifname eth1 type ethernet ipv4.address 192.0.2.100/24 ipv4.gateway 192.0.2.1
|
||||
options = {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.address': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.address': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
|
@ -1054,8 +1079,10 @@ class Nmcli(object):
|
|||
cmd = [self.nmcli_bin, 'con', 'mod', self.conn_name]
|
||||
|
||||
options = {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.address': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.address': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
|
@ -1166,9 +1193,11 @@ class Nmcli(object):
|
|||
|
||||
params = {'vlan.parent': self.vlandev,
|
||||
'vlan.id': self.vlanid,
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.address': self.ip4 or '',
|
||||
'ipv4.gateway': self.gw4 or '',
|
||||
'ipv4.dns': self.dns4 or '',
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.address': self.ip6 or '',
|
||||
'ipv6.gateway': self.gw6 or '',
|
||||
'ipv6.dns': self.dns6 or '',
|
||||
|
@ -1420,6 +1449,257 @@ class Nmcli(object):
|
|||
self.module.fail_json(msg="Type of device or network connection is required "
|
||||
"while performing 'modify' operation. Please specify 'type' as an argument.")
|
||||
|
||||
def show_connection(self):
|
||||
cmd = [self.nmcli_bin, 'con', 'show']
|
||||
if self.conn_name is not None:
|
||||
cmd.append(self.conn_name)
|
||||
elif self.ifname is not None:
|
||||
cmd.append(self.ifname)
|
||||
else:
|
||||
if self.type == 'vlan':
|
||||
cmd.append('vlan%s' % to_text(self.vlanid))
|
||||
elif self.type == 'vxlan':
|
||||
cmd.append('vxlan%s' % to_text(self.vxlan_id))
|
||||
elif self.type == 'ipip':
|
||||
cmd.append('ipip%s' % self.ip_tunnel_dev)
|
||||
elif self.type == 'sit':
|
||||
cmd.append('sit%s' % self.ip_tunnel_dev)
|
||||
else:
|
||||
raise NmcliModuleError(
|
||||
"Invalid connection identifier for nmcli")
|
||||
|
||||
(rc, out, err) = self.execute_command(cmd)
|
||||
|
||||
if rc != 0:
|
||||
raise NmcliModuleError(err)
|
||||
|
||||
p_enum_value = re.compile(r'^([-]?\d+) \((\w+)\)$')
|
||||
|
||||
conn_info = dict()
|
||||
for line in out.splitlines():
|
||||
pair = line.split(':', 1)
|
||||
key = pair[0].strip()
|
||||
if key and len(pair) > 1:
|
||||
raw_value = pair[1].lstrip()
|
||||
if raw_value == '--':
|
||||
conn_info[key] = None
|
||||
elif key == 'bond.options':
|
||||
# Aliases such as 'miimon', 'downdelay' are equivalent to the +bond.options 'option=value' syntax.
|
||||
opts = raw_value.split(',')
|
||||
for opt in opts:
|
||||
alias_pair = opt.split('=', 1)
|
||||
if len(alias_pair) > 1:
|
||||
alias_key = alias_pair[0]
|
||||
alias_value = alias_pair[1]
|
||||
conn_info[alias_key] = alias_value
|
||||
elif key in ['ipv4.dns', 'ipv4.dns-search', 'ipv6.dns', 'ipv6.dns-search']:
|
||||
values = raw_value.split(',')
|
||||
conn_info[key] = values
|
||||
else:
|
||||
m_enum = p_enum_value.match(raw_value)
|
||||
if m_enum is not None:
|
||||
value = m_enum.group(1)
|
||||
else:
|
||||
value = raw_value
|
||||
conn_info[key] = value
|
||||
|
||||
return conn_info
|
||||
|
||||
def _compare_conn_params(self, conn_info, options):
|
||||
# See nmcli(1) for details
|
||||
param_alias = {
|
||||
'type': 'connection.type',
|
||||
'con-name': 'connection.id',
|
||||
'autoconnect': 'connection.autoconnect',
|
||||
'ifname': 'connection.interface-name',
|
||||
'master': 'connection.master',
|
||||
'slave-type': 'connection.slave-type',
|
||||
}
|
||||
|
||||
changed = False
|
||||
diff_before = dict()
|
||||
diff_after = dict()
|
||||
|
||||
for key, value in options.items():
|
||||
if not value:
|
||||
continue
|
||||
|
||||
# TODO: retain typed list arguments in Nmcli object instead of encoded strings
|
||||
if key in ['ipv4.dns', 'ipv4.dns-search', 'ipv6.dns', 'ipv6.dns-search']:
|
||||
list_values = value.split()
|
||||
value = list_values
|
||||
|
||||
if key in conn_info:
|
||||
current_value = conn_info[key]
|
||||
elif key in param_alias:
|
||||
real_key = param_alias[key]
|
||||
if real_key in conn_info:
|
||||
current_value = conn_info[real_key]
|
||||
else:
|
||||
# alias parameter does not exist
|
||||
current_value = None
|
||||
else:
|
||||
# parameter does not exist
|
||||
current_value = None
|
||||
|
||||
if isinstance(current_value, list) and isinstance(value, list):
|
||||
# compare values between two lists
|
||||
if sorted(current_value) != sorted(value):
|
||||
changed = True
|
||||
else:
|
||||
if current_value != to_text(value):
|
||||
changed = True
|
||||
|
||||
diff_before[key] = current_value
|
||||
diff_after[key] = value
|
||||
|
||||
diff = {
|
||||
'before': diff_before,
|
||||
'after': diff_after,
|
||||
}
|
||||
return (changed, diff)
|
||||
|
||||
def is_connection_changed(self):
|
||||
conn_info = self.show_connection()
|
||||
changed = False
|
||||
|
||||
if self.type == 'team':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.addresses': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.addresses': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
'ipv4.dns-search': self.dns4_search,
|
||||
'ipv6.dns-search': self.dns6_search,
|
||||
'ipv4.dhcp-client-id': self.dhcp_client_id,
|
||||
})
|
||||
elif self.type == 'team-slave':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'connection.master': self.master,
|
||||
'802-3-ethernet.mtu': to_text(self.mtu)
|
||||
})
|
||||
elif self.type == 'bond':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.addresses': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.addresses': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
'ipv4.dns-search': self.dns4_search,
|
||||
'ipv6.dns-search': self.dns6_search,
|
||||
'miimon': self.miimon,
|
||||
'downdelay': self.downdelay,
|
||||
'updelay': self.updelay,
|
||||
'arp-interval': self.arp_interval,
|
||||
'arp-ip-target': self.arp_ip_target,
|
||||
'ipv4.dhcp-client-id': self.dhcp_client_id,
|
||||
})
|
||||
elif self.type == 'bond-slave':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'connection.master': self.master,
|
||||
})
|
||||
elif self.type == 'ethernet':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.addresses': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.addresses': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
'ipv4.dns-search': self.dns4_search,
|
||||
'ipv6.dns-search': self.dns6_search,
|
||||
'802-3-ethernet.mtu': self.mtu,
|
||||
'ipv4.dhcp-client-id': self.dhcp_client_id,
|
||||
})
|
||||
elif self.type == 'bridge':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.addresses': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.addresses': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
'bridge.ageing-time': self.ageingtime,
|
||||
'bridge.forward-delay': self.forwarddelay,
|
||||
'bridge.hello-time': self.hellotime,
|
||||
'bridge.mac-address': self.mac,
|
||||
'bridge.max-age': self.maxage,
|
||||
'bridge.priority': self.priority,
|
||||
'bridge.stp': self.bool_to_string(self.stp)
|
||||
})
|
||||
elif self.type == 'bridge-slave':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'master': self.master,
|
||||
'bridge-port.path-cost': self.path_cost,
|
||||
'bridge-port.hairpin-mode': self.bool_to_string(self.hairpin),
|
||||
'bridge-port.priority': self.slavepriority,
|
||||
})
|
||||
elif self.type == 'vlan':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'vlan.parent': self.vlandev,
|
||||
'vlan.id': self.vlanid,
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.addresses': self.ip4 or '',
|
||||
'ipv4.gateway': self.gw4 or '',
|
||||
'ipv4.dns': self.dns4 or '',
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.addresses': self.ip6 or '',
|
||||
'ipv6.gateway': self.gw6 or '',
|
||||
'ipv6.dns': self.dns6 or '',
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
})
|
||||
elif self.type == 'vxlan':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'vxlan.id': self.vxlan_id,
|
||||
'vxlan.local': self.vxlan_local,
|
||||
'vxlan.remote': self.vxlan_remote,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
})
|
||||
elif self.type == 'ipip':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ip-tunnel.local': self.ip_tunnel_local,
|
||||
'ip-tunnel.remote': self.ip_tunnel_remote,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
})
|
||||
elif self.type == 'sit':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ip-tunnel.local': self.ip_tunnel_local,
|
||||
'ip-tunnel.remote': self.ip_tunnel_remote,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
})
|
||||
elif self.type == 'generic':
|
||||
changed, diff = self._compare_conn_params(conn_info, {
|
||||
'ipv4.method': self.ipv4_method,
|
||||
'ipv4.addresses': self.ip4,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.dns': self.dns4,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.addresses': self.ip6,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.dns': self.dns6,
|
||||
'autoconnect': self.bool_to_string(self.autoconnect),
|
||||
'ipv4.dns-search': self.dns4_search,
|
||||
'ipv6.dns-search': self.dns6_search,
|
||||
'ipv4.dhcp-client-id': self.dhcp_client_id,
|
||||
})
|
||||
else:
|
||||
raise NmcliModuleError("Unknown type of device: %s" % self.type)
|
||||
|
||||
return changed, diff
|
||||
|
||||
|
||||
def main():
|
||||
# Parsing argument file
|
||||
|
@ -1502,30 +1782,42 @@ def main():
|
|||
if nmcli.ifname is None:
|
||||
nmcli.module.fail_json(msg="Please specify an interface name for the connection when type is %s" % nmcli.type)
|
||||
|
||||
if nmcli.state == 'absent':
|
||||
if nmcli.connection_exists():
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = nmcli.down_connection()
|
||||
(rc, out, err) = nmcli.remove_connection()
|
||||
if rc != 0:
|
||||
module.fail_json(name=('No Connection named %s exists' % nmcli.conn_name), msg=err, rc=rc)
|
||||
try:
|
||||
if nmcli.state == 'absent':
|
||||
if nmcli.connection_exists():
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = nmcli.down_connection()
|
||||
(rc, out, err) = nmcli.remove_connection()
|
||||
if rc != 0:
|
||||
module.fail_json(name=('No Connection named %s exists' % nmcli.conn_name), msg=err, rc=rc)
|
||||
|
||||
elif nmcli.state == 'present':
|
||||
if nmcli.connection_exists():
|
||||
# modify connection (note: this function is check mode aware)
|
||||
# result['Connection']=('Connection %s of Type %s is not being added' % (nmcli.conn_name, nmcli.type))
|
||||
result['Exists'] = 'Connections do exist so we are modifying them'
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = nmcli.modify_connection()
|
||||
if not nmcli.connection_exists():
|
||||
result['Connection'] = ('Connection %s of Type %s is being added' % (nmcli.conn_name, nmcli.type))
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
(rc, out, err) = nmcli.create_connection()
|
||||
if rc is not None and rc != 0:
|
||||
module.fail_json(name=nmcli.conn_name, msg=err, rc=rc)
|
||||
elif nmcli.state == 'present':
|
||||
if nmcli.connection_exists():
|
||||
changed, diff = nmcli.is_connection_changed()
|
||||
if module._diff:
|
||||
result['diff'] = diff
|
||||
|
||||
if changed:
|
||||
# modify connection (note: this function is check mode aware)
|
||||
# result['Connection']=('Connection %s of Type %s is not being added' % (nmcli.conn_name, nmcli.type))
|
||||
result['Exists'] = 'Connections do exist so we are modifying them'
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True, **result)
|
||||
(rc, out, err) = nmcli.modify_connection()
|
||||
else:
|
||||
result['Exists'] = 'Connections already exist and no changes made'
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False, **result)
|
||||
if not nmcli.connection_exists():
|
||||
result['Connection'] = ('Connection %s of Type %s is being added' % (nmcli.conn_name, nmcli.type))
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True, **result)
|
||||
(rc, out, err) = nmcli.create_connection()
|
||||
if rc is not None and rc != 0:
|
||||
module.fail_json(name=nmcli.conn_name, msg=err, rc=rc)
|
||||
except NmcliModuleError as e:
|
||||
module.fail_json(name=nmcli.conn_name, msg=str(e))
|
||||
|
||||
if rc is None:
|
||||
result['changed'] = False
|
||||
|
|
|
@ -81,19 +81,29 @@ TESTCASE_GENERIC = [
|
|||
'type': 'generic',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'generic_non_existant',
|
||||
'ip4': '10.10.10.10',
|
||||
'ip4': '10.10.10.10/24',
|
||||
'gw4': '10.10.10.1',
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_GENERIC_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: generic_non_existant
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: manual
|
||||
ipv4.addresses: 10.10.10.10/24
|
||||
ipv4.gateway: 10.10.10.1
|
||||
ipv6.method: auto
|
||||
"""
|
||||
|
||||
TESTCASE_GENERIC_DNS4_SEARCH = [
|
||||
{
|
||||
'type': 'generic',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'generic_non_existant',
|
||||
'ip4': '10.10.10.10',
|
||||
'ip4': '10.10.10.10/24',
|
||||
'gw4': '10.10.10.1',
|
||||
'state': 'present',
|
||||
'dns4_search': 'search.redhat.com',
|
||||
|
@ -102,13 +112,25 @@ TESTCASE_GENERIC_DNS4_SEARCH = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_GENERIC_DNS4_SEARCH_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: generic_non_existant
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: manual
|
||||
ipv4.addresses: 10.10.10.10/24
|
||||
ipv4.gateway: 10.10.10.1
|
||||
ipv4.dns-search: search.redhat.com
|
||||
ipv6.dns-search: search6.redhat.com
|
||||
ipv6.method: auto
|
||||
"""
|
||||
|
||||
TESTCASE_BOND = [
|
||||
{
|
||||
'type': 'bond',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'bond_non_existant',
|
||||
'mode': 'active-backup',
|
||||
'ip4': '10.10.10.10',
|
||||
'ip4': '10.10.10.10/24',
|
||||
'gw4': '10.10.10.1',
|
||||
'state': 'present',
|
||||
'primary': 'non_existent_primary',
|
||||
|
@ -116,12 +138,23 @@ TESTCASE_BOND = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_BOND_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: bond_non_existant
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: manual
|
||||
ipv4.addresses: 10.10.10.10/24
|
||||
ipv4.gateway: 10.10.10.1
|
||||
ipv6.method: auto
|
||||
bond.options: mode=active-backup,primary=non_existent_primary
|
||||
"""
|
||||
|
||||
TESTCASE_BRIDGE = [
|
||||
{
|
||||
'type': 'bridge',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'br0_non_existant',
|
||||
'ip4': '10.10.10.10',
|
||||
'ip4': '10.10.10.10/24',
|
||||
'gw4': '10.10.10.1',
|
||||
'maxage': 100,
|
||||
'stp': True,
|
||||
|
@ -130,6 +163,22 @@ TESTCASE_BRIDGE = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_BRIDGE_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: br0_non_existant
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: manual
|
||||
ipv4.addresses: 10.10.10.10/24
|
||||
ipv4.gateway: 10.10.10.1
|
||||
ipv6.method: auto
|
||||
bridge.stp: yes
|
||||
bridge.max-age: 100
|
||||
bridge.ageing-time: 300
|
||||
bridge.hello-time: 2
|
||||
bridge.priority: 128
|
||||
bridge.forward-delay: 15
|
||||
"""
|
||||
|
||||
TESTCASE_BRIDGE_SLAVE = [
|
||||
{
|
||||
'type': 'bridge-slave',
|
||||
|
@ -141,12 +190,21 @@ TESTCASE_BRIDGE_SLAVE = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_BRIDGE_SLAVE_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: br0_non_existant
|
||||
connection.autoconnect: yes
|
||||
bridge-port.path-cost: 100
|
||||
bridge-port.hairpin-mode: yes
|
||||
bridge-port.priority: 32
|
||||
"""
|
||||
|
||||
TESTCASE_VLAN = [
|
||||
{
|
||||
'type': 'vlan',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'vlan_not_exists',
|
||||
'ip4': '10.10.10.10',
|
||||
'ip4': '10.10.10.10/24',
|
||||
'gw4': '10.10.10.1',
|
||||
'vlanid': 10,
|
||||
'state': 'present',
|
||||
|
@ -154,6 +212,17 @@ TESTCASE_VLAN = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_VLAN_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: vlan_not_exists
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: manual
|
||||
ipv4.addresses: 10.10.10.10/24
|
||||
ipv4.gateway: 10.10.10.1
|
||||
ipv6.method: auto
|
||||
vlan.id: 10
|
||||
"""
|
||||
|
||||
TESTCASE_VXLAN = [
|
||||
{
|
||||
'type': 'vxlan',
|
||||
|
@ -167,6 +236,15 @@ TESTCASE_VXLAN = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_VXLAN_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: existent_nw_device
|
||||
connection.autoconnect: yes
|
||||
vxlan.id: 11
|
||||
vxlan.local: 192.168.225.5
|
||||
vxlan.remote: 192.168.225.6
|
||||
"""
|
||||
|
||||
TESTCASE_IPIP = [
|
||||
{
|
||||
'type': 'ipip',
|
||||
|
@ -180,6 +258,16 @@ TESTCASE_IPIP = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_IPIP_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: ipip-existent_nw_device
|
||||
connection.autoconnect: yes
|
||||
ip-tunnel.mode: ipip
|
||||
ip-tunnel.parent: non_existent_ipip_device
|
||||
ip-tunnel.local: 192.168.225.5
|
||||
ip-tunnel.remote: 192.168.225.6
|
||||
"""
|
||||
|
||||
TESTCASE_SIT = [
|
||||
{
|
||||
'type': 'sit',
|
||||
|
@ -193,19 +281,60 @@ TESTCASE_SIT = [
|
|||
}
|
||||
]
|
||||
|
||||
TESTCASE_SIT_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: sit-existent_nw_device
|
||||
connection.autoconnect: yes
|
||||
ip-tunnel.mode: sit
|
||||
ip-tunnel.parent: non_existent_sit_device
|
||||
ip-tunnel.local: 192.168.225.5
|
||||
ip-tunnel.remote: 192.168.225.6
|
||||
"""
|
||||
|
||||
TESTCASE_ETHERNET_DHCP = [
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'ethernet_non_existant',
|
||||
'ip4': '10.10.10.10',
|
||||
'gw4': '10.10.10.1',
|
||||
'dhcp_client_id': '00:11:22:AA:BB:CC:DD',
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
'dhcp_client_id': '00:11:22:AA:BB:CC:DD',
|
||||
}
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_DHCP_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: ethernet_non_existant
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: auto
|
||||
ipv4.dhcp-client-id: 00:11:22:AA:BB:CC:DD
|
||||
ipv6.method: auto
|
||||
"""
|
||||
|
||||
TESTCASE_ETHERNET_STATIC = [
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'ethernet_non_existant',
|
||||
'ip4': '10.10.10.10/24',
|
||||
'gw4': '10.10.10.1',
|
||||
'dns4': ['1.1.1.1', '8.8.8.8'],
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
}
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT = """\
|
||||
connection.id: non_existent_nw_device
|
||||
connection.interface-name: ethernet_non_existant
|
||||
connection.autoconnect: yes
|
||||
ipv4.method: manual
|
||||
ipv4.addresses: 10.10.10.10/24
|
||||
ipv4.gateway: 10.10.10.1
|
||||
ipv4.dns: 1.1.1.1,8.8.8.8
|
||||
ipv6.method: auto
|
||||
"""
|
||||
|
||||
|
||||
def mocker_set(mocker, connection_exists=False):
|
||||
"""
|
||||
|
@ -224,15 +353,7 @@ def mocker_set(mocker, connection_exists=False):
|
|||
def mocked_generic_connection_create(mocker):
|
||||
mocker_set(mocker)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = {"rc": 100, "out": "aaa", "err": "none"}
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_generic_connection_modify(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = {"rc": 100, "out": "aaa", "err": "none"}
|
||||
command_result.return_value = (0, "", "")
|
||||
return command_result
|
||||
|
||||
|
||||
|
@ -242,8 +363,127 @@ def mocked_connection_exists(mocker):
|
|||
return connection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_generic_connection_modify(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
connection_changed = mocker.patch.object(
|
||||
nmcli.Nmcli, 'is_connection_changed')
|
||||
connection_changed.return_value = (True, dict())
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, "", "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_generic_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_GENERIC_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_generic_connection_dns_search_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (
|
||||
0, TESTCASE_GENERIC_DNS4_SEARCH_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_bond_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_BOND_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_bridge_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_BRIDGE_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_bridge_slave_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_BRIDGE_SLAVE_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_vlan_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_VLAN_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_vxlan_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_VXLAN_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_ipip_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_IPIP_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_sit_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_SIT_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_ethernet_connection_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_ETHERNET_DHCP, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_ethernet_connection_dhcp_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_ETHERNET_DHCP_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_ethernet_connection_static_unchanged(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.return_value = (0, TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT, "")
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mocked_ethernet_connection_dhcp_to_static(mocker):
|
||||
mocker_set(mocker, connection_exists=True)
|
||||
command_result = mocker.patch.object(nmcli.Nmcli, 'execute_command')
|
||||
command_result.side_effect = [
|
||||
(0, TESTCASE_ETHERNET_DHCP_SHOW_OUTPUT, ""),
|
||||
(0, "", ""),
|
||||
]
|
||||
return command_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BOND, indirect=['patch_ansible_module'])
|
||||
def test_bond_connection_create(mocked_generic_connection_create):
|
||||
def test_bond_connection_create(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test : Bond connection created
|
||||
"""
|
||||
|
@ -267,9 +507,28 @@ def test_bond_connection_create(mocked_generic_connection_create):
|
|||
for param in ['gw4', 'primary', 'autoconnect', 'mode', 'active-backup', 'ip4']:
|
||||
assert param in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BOND, indirect=['patch_ansible_module'])
|
||||
def test_bond_connection_unchanged(mocked_bond_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : Bond connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC, indirect=['patch_ansible_module'])
|
||||
def test_generic_connection_create(mocked_generic_connection_create):
|
||||
def test_generic_connection_create(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test : Generic connection created
|
||||
"""
|
||||
|
@ -291,9 +550,14 @@ def test_generic_connection_create(mocked_generic_connection_create):
|
|||
for param in ['autoconnect', 'gw4', 'ip4']:
|
||||
assert param in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC, indirect=['patch_ansible_module'])
|
||||
def test_generic_connection_modify(mocked_generic_connection_modify):
|
||||
def test_generic_connection_modify(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test : Generic connection modify
|
||||
"""
|
||||
|
@ -312,9 +576,28 @@ def test_generic_connection_modify(mocked_generic_connection_modify):
|
|||
for param in ['ipv4.gateway', 'ipv4.address']:
|
||||
assert param in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC, indirect=['patch_ansible_module'])
|
||||
def test_generic_connection_unchanged(mocked_generic_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : Generic connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DNS4_SEARCH, indirect=['patch_ansible_module'])
|
||||
def test_generic_connection_create_dns_search(mocked_generic_connection_create):
|
||||
def test_generic_connection_create_dns_search(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test : Generic connection created with dns search
|
||||
"""
|
||||
|
@ -328,9 +611,14 @@ def test_generic_connection_create_dns_search(mocked_generic_connection_create):
|
|||
assert 'ipv4.dns-search' in args[0]
|
||||
assert 'ipv6.dns-search' in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DNS4_SEARCH, indirect=['patch_ansible_module'])
|
||||
def test_generic_connection_modify_dns_search(mocked_generic_connection_create):
|
||||
def test_generic_connection_modify_dns_search(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test : Generic connection modified with dns search
|
||||
"""
|
||||
|
@ -344,6 +632,25 @@ def test_generic_connection_modify_dns_search(mocked_generic_connection_create):
|
|||
assert 'ipv4.dns-search' in args[0]
|
||||
assert 'ipv6.dns-search' in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DNS4_SEARCH, indirect=['patch_ansible_module'])
|
||||
def test_generic_connection_dns_search_unchanged(mocked_generic_connection_dns_search_unchanged, capfd):
|
||||
"""
|
||||
Test : Generic connection with dns search unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_CONNECTION, indirect=['patch_ansible_module'])
|
||||
def test_dns4_none(mocked_connection_exists, capfd):
|
||||
|
@ -355,11 +662,12 @@ def test_dns4_none(mocked_connection_exists, capfd):
|
|||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE, indirect=['patch_ansible_module'])
|
||||
def test_create_bridge(mocked_generic_connection_create):
|
||||
def test_create_bridge(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test if Bridge created
|
||||
"""
|
||||
|
@ -378,12 +686,17 @@ def test_create_bridge(mocked_generic_connection_create):
|
|||
assert args[0][5] == 'con-name'
|
||||
assert args[0][6] == 'non_existent_nw_device'
|
||||
|
||||
for param in ['ip4', '10.10.10.10', 'gw4', '10.10.10.1', 'bridge.max-age', '100', 'bridge.stp', 'yes']:
|
||||
for param in ['ip4', '10.10.10.10/24', 'gw4', '10.10.10.1', 'bridge.max-age', '100', 'bridge.stp', 'yes']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE, indirect=['patch_ansible_module'])
|
||||
def test_mod_bridge(mocked_generic_connection_modify):
|
||||
def test_mod_bridge(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test if Bridge modified
|
||||
"""
|
||||
|
@ -399,12 +712,31 @@ def test_mod_bridge(mocked_generic_connection_modify):
|
|||
assert args[0][1] == 'con'
|
||||
assert args[0][2] == 'mod'
|
||||
assert args[0][3] == 'non_existent_nw_device'
|
||||
for param in ['ipv4.address', '10.10.10.10', 'ipv4.gateway', '10.10.10.1', 'bridge.max-age', '100', 'bridge.stp', 'yes']:
|
||||
for param in ['ipv4.address', '10.10.10.10/24', 'ipv4.gateway', '10.10.10.1', 'bridge.max-age', '100', 'bridge.stp', 'yes']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE, indirect=['patch_ansible_module'])
|
||||
def test_bridge_connection_unchanged(mocked_bridge_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : Bridge connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE_SLAVE, indirect=['patch_ansible_module'])
|
||||
def test_create_bridge_slave(mocked_generic_connection_create):
|
||||
def test_create_bridge_slave(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test if Bridge_slave created
|
||||
"""
|
||||
|
@ -427,9 +759,14 @@ def test_create_bridge_slave(mocked_generic_connection_create):
|
|||
for param in ['bridge-port.path-cost', '100']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE_SLAVE, indirect=['patch_ansible_module'])
|
||||
def test_mod_bridge_slave(mocked_generic_connection_modify):
|
||||
def test_mod_bridge_slave(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test if Bridge_slave modified
|
||||
"""
|
||||
|
@ -449,9 +786,28 @@ def test_mod_bridge_slave(mocked_generic_connection_modify):
|
|||
for param in ['bridge-port.path-cost', '100']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE_SLAVE, indirect=['patch_ansible_module'])
|
||||
def test_bridge_slave_unchanged(mocked_bridge_slave_unchanged, capfd):
|
||||
"""
|
||||
Test : Bridge-slave connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VLAN, indirect=['patch_ansible_module'])
|
||||
def test_create_vlan_con(mocked_generic_connection_create):
|
||||
def test_create_vlan_con(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test if VLAN created
|
||||
"""
|
||||
|
@ -471,12 +827,17 @@ def test_create_vlan_con(mocked_generic_connection_create):
|
|||
assert args[0][5] == 'con-name'
|
||||
assert args[0][6] == 'non_existent_nw_device'
|
||||
|
||||
for param in ['ip4', '10.10.10.10', 'gw4', '10.10.10.1', 'id', '10']:
|
||||
for param in ['ip4', '10.10.10.10/24', 'gw4', '10.10.10.1', 'id', '10']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VLAN, indirect=['patch_ansible_module'])
|
||||
def test_mod_vlan_conn(mocked_generic_connection_modify):
|
||||
def test_mod_vlan_conn(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test if VLAN modified
|
||||
"""
|
||||
|
@ -493,12 +854,31 @@ def test_mod_vlan_conn(mocked_generic_connection_modify):
|
|||
assert args[0][2] == 'mod'
|
||||
assert args[0][3] == 'non_existent_nw_device'
|
||||
|
||||
for param in ['ipv4.address', '10.10.10.10', 'ipv4.gateway', '10.10.10.1', 'vlan.id', '10']:
|
||||
for param in ['ipv4.address', '10.10.10.10/24', 'ipv4.gateway', '10.10.10.1', 'vlan.id', '10']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VLAN, indirect=['patch_ansible_module'])
|
||||
def test_vlan_connection_unchanged(mocked_vlan_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : VLAN connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VXLAN, indirect=['patch_ansible_module'])
|
||||
def test_create_vxlan(mocked_generic_connection_create):
|
||||
def test_create_vxlan(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test if vxlan created
|
||||
"""
|
||||
|
@ -521,9 +901,14 @@ def test_create_vxlan(mocked_generic_connection_create):
|
|||
for param in ['vxlan.local', '192.168.225.5', 'vxlan.remote', '192.168.225.6', 'vxlan.id', '11']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VXLAN, indirect=['patch_ansible_module'])
|
||||
def test_vxlan_mod(mocked_generic_connection_modify):
|
||||
def test_vxlan_mod(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test if vxlan modified
|
||||
"""
|
||||
|
@ -542,9 +927,28 @@ def test_vxlan_mod(mocked_generic_connection_modify):
|
|||
for param in ['vxlan.local', '192.168.225.5', 'vxlan.remote', '192.168.225.6', 'vxlan.id', '11']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VXLAN, indirect=['patch_ansible_module'])
|
||||
def test_vxlan_connection_unchanged(mocked_vxlan_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : VxLAN connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_IPIP, indirect=['patch_ansible_module'])
|
||||
def test_create_ipip(mocked_generic_connection_create):
|
||||
def test_create_ipip(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test if ipip created
|
||||
"""
|
||||
|
@ -572,9 +976,14 @@ def test_create_ipip(mocked_generic_connection_create):
|
|||
for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_IPIP, indirect=['patch_ansible_module'])
|
||||
def test_ipip_mod(mocked_generic_connection_modify):
|
||||
def test_ipip_mod(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test if ipip modified
|
||||
"""
|
||||
|
@ -593,9 +1002,28 @@ def test_ipip_mod(mocked_generic_connection_modify):
|
|||
for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_IPIP, indirect=['patch_ansible_module'])
|
||||
def test_ipip_connection_unchanged(mocked_ipip_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : IPIP connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SIT, indirect=['patch_ansible_module'])
|
||||
def test_create_sit(mocked_generic_connection_create):
|
||||
def test_create_sit(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test if sit created
|
||||
"""
|
||||
|
@ -623,9 +1051,14 @@ def test_create_sit(mocked_generic_connection_create):
|
|||
for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SIT, indirect=['patch_ansible_module'])
|
||||
def test_sit_mod(mocked_generic_connection_modify):
|
||||
def test_sit_mod(mocked_generic_connection_modify, capfd):
|
||||
"""
|
||||
Test if sit modified
|
||||
"""
|
||||
|
@ -644,9 +1077,28 @@ def test_sit_mod(mocked_generic_connection_modify):
|
|||
for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']:
|
||||
assert param in map(to_text, args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SIT, indirect=['patch_ansible_module'])
|
||||
def test_sit_connection_unchanged(mocked_sit_connection_unchanged, capfd):
|
||||
"""
|
||||
Test : SIT connection unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_DHCP, indirect=['patch_ansible_module'])
|
||||
def test_eth_dhcp_client_id_con_create(mocked_generic_connection_create):
|
||||
def test_eth_dhcp_client_id_con_create(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test : Ethernet connection created with DHCP_CLIENT_ID
|
||||
"""
|
||||
|
@ -658,3 +1110,103 @@ def test_eth_dhcp_client_id_con_create(mocked_generic_connection_create):
|
|||
args, kwargs = arg_list[0]
|
||||
|
||||
assert 'ipv4.dhcp-client-id' in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_DHCP, indirect=['patch_ansible_module'])
|
||||
def test_ethernet_connection_dhcp_unchanged(mocked_ethernet_connection_dhcp_unchanged, capfd):
|
||||
"""
|
||||
Test : Ethernet connection with DHCP_CLIENT_ID unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC, indirect=['patch_ansible_module'])
|
||||
def test_modify_ethernet_dhcp_to_static(mocked_ethernet_connection_dhcp_to_static, capfd):
|
||||
"""
|
||||
Test : Modify ethernet connection from DHCP to static
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
assert nmcli.Nmcli.execute_command.call_count == 2
|
||||
arg_list = nmcli.Nmcli.execute_command.call_args_list
|
||||
args, kwargs = arg_list[1]
|
||||
|
||||
assert args[0][0] == '/usr/bin/nmcli'
|
||||
assert args[0][1] == 'con'
|
||||
assert args[0][2] == 'mod'
|
||||
assert args[0][3] == 'non_existent_nw_device'
|
||||
|
||||
for param in ['ipv4.method', 'ipv4.gateway', 'ipv4.address']:
|
||||
assert param in args[0]
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC, indirect=['patch_ansible_module'])
|
||||
def test_create_ethernet_static(mocked_generic_connection_create, capfd):
|
||||
"""
|
||||
Test : Create ethernet connection with static IP configuration
|
||||
"""
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
assert nmcli.Nmcli.execute_command.call_count == 3
|
||||
arg_list = nmcli.Nmcli.execute_command.call_args_list
|
||||
add_args, add_kw = arg_list[0]
|
||||
mod_args, mod_kw = arg_list[1]
|
||||
|
||||
assert add_args[0][0] == '/usr/bin/nmcli'
|
||||
assert add_args[0][1] == 'con'
|
||||
assert add_args[0][2] == 'add'
|
||||
assert add_args[0][3] == 'type'
|
||||
assert add_args[0][4] == 'ethernet'
|
||||
assert add_args[0][5] == 'con-name'
|
||||
assert add_args[0][6] == 'non_existent_nw_device'
|
||||
assert add_args[0][7] == 'ifname'
|
||||
assert add_args[0][8] == 'ethernet_non_existant'
|
||||
|
||||
for param in ['ip4', '10.10.10.10/24', 'gw4', '10.10.10.1']:
|
||||
assert param in map(to_text, add_args[0])
|
||||
|
||||
assert mod_args[0][0] == '/usr/bin/nmcli'
|
||||
assert mod_args[0][1] == 'con'
|
||||
assert mod_args[0][2] == 'mod'
|
||||
assert mod_args[0][3] == 'non_existent_nw_device'
|
||||
|
||||
for param in ['ipv4.dns', '1.1.1.1 8.8.8.8']:
|
||||
assert param in map(to_text, mod_args[0])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert results['changed']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC, indirect=['patch_ansible_module'])
|
||||
def test_ethernet_connection_static_unchanged(mocked_ethernet_connection_static_unchanged, capfd):
|
||||
"""
|
||||
Test : Ethernet connection with static IP configuration unchanged
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
nmcli.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert not results.get('failed')
|
||||
assert not results['changed']
|
||||
|
|
Loading…
Reference in a new issue