diff --git a/lib/ansible/modules/net_tools/nmcli.py b/lib/ansible/modules/net_tools/nmcli.py index 5e953a54a35..ce25e2d5182 100644 --- a/lib/ansible/modules/net_tools/nmcli.py +++ b/lib/ansible/modules/net_tools/nmcli.py @@ -53,7 +53,7 @@ options: description: - This is the type of device or network connection that you wish to create or modify. - "type C(generic) is added in version 2.5." - choices: [ ethernet, team, team-slave, bond, bond-slave, bridge, bridge-slave, vlan, generic ] + choices: [ ethernet, team, team-slave, bond, bond-slave, bridge, bridge-slave, vlan, vxlan, generic ] mode: description: - This is the type of device or network connection that you wish to create for a bond, team or bridge. @@ -174,7 +174,18 @@ options: egress: description: - This is only used with VLAN - VLAN egress priority mapping - + vxlan_id: + description: + - This is only used with VXLAN - VXLAN ID. + version_added: "2.8" + vxlan_remote: + description: + - This is only used with VXLAN - VXLAN destination IP address. + version_added: "2.8" + vxlan_local: + description: + - This is only used with VXLAN - VXLAN local IP address. + version_added: "2.8" ''' EXAMPLES = ''' @@ -434,6 +445,14 @@ EXAMPLES = ''' type: ethernet state: present +# To add VxLan, issue a command as follows: + - nmcli: + type: vxlan + conn_name: vxlan_test1 + vxlan_id: 16 + vxlan_local: 192.168.1.2 + vxlan_remote: 192.168.1.5 + # nmcli exits with status 0 if it succeeds and exits with a status greater # than zero when there is a failure. The following list of status codes may be # returned: @@ -505,7 +524,8 @@ class Nmcli(object): 12: "ADSL", 13: "Bridge", 14: "Generic", - 15: "Team" + 15: "Team", + 16: "VxLan", } STATES = { 0: "Unknown", @@ -562,6 +582,9 @@ class Nmcli(object): self.flags = module.params['flags'] self.ingress = module.params['ingress'] self.egress = module.params['egress'] + self.vxlan_id = module.params['vxlan_id'] + self.vxlan_local = module.params['vxlan_local'] + self.vxlan_remote = module.params['vxlan_remote'] self.nmcli_bin = self.module.get_bin_path('nmcli', True) self.dhcp_client_id = module.params['dhcp_client_id'] @@ -1072,6 +1095,53 @@ class Nmcli(object): return cmd + def create_connection_vxlan(self): + cmd = [self.nmcli_bin, 'con', 'add', 'type', 'vxlan', 'con-name'] + + if self.conn_name is not None: + cmd.append(self.conn_name) + elif self.ifname is not None: + cmd.append(self.ifname) + else: + cmd.append('vxlan%s' % self.vxlanid) + + cmd.append('ifname') + if self.ifname is not None: + cmd.append(self.ifname) + elif self.conn_name is not None: + cmd.append(self.conn_name) + else: + cmd.append('vxan%s' % self.vxlanid) + + params = {'vxlan.id': self.vxlan_id, + 'vxlan.local': self.vxlan_local, + 'vxlan.remote': self.vxlan_remote, + 'autoconnect': self.bool_to_string(self.autoconnect) + } + for k, v in params.items(): + cmd.extend([k, v]) + + return cmd + + def modify_connection_vxlan(self): + cmd = [self.nmcli_bin, 'con', 'mod'] + + if self.conn_name is not None: + cmd.append(self.conn_name) + elif self.ifname is not None: + cmd.append(self.ifname) + else: + cmd.append('vxlan%s' % self.vxlanid) + + params = {'vxlan.id': self.vxlan_id, + 'vxlan.local': self.vxlan_local, + 'vxlan.remote': self.vxlan_remote, + 'autoconnect': self.bool_to_string(self.autoconnect) + } + for k, v in params.items(): + cmd.extend([k, v]) + return cmd + def create_connection(self): cmd = [] if self.type == 'team': @@ -1117,6 +1187,8 @@ class Nmcli(object): cmd = self.create_connection_bridge_slave() elif self.type == 'vlan': cmd = self.create_connection_vlan() + elif self.type == 'vxlan': + cmd = self.create_connection_vxlan() elif self.type == 'generic': cmd = self.create_connection_ethernet(conn_type='generic') @@ -1149,6 +1221,8 @@ class Nmcli(object): cmd = self.modify_connection_bridge_slave() elif self.type == 'vlan': cmd = self.modify_connection_vlan() + elif self.type == 'vxlan': + cmd = self.modify_connection_vxlan() elif self.type == 'generic': cmd = self.modify_connection_ethernet(conn_type='generic') if cmd: @@ -1170,7 +1244,7 @@ def main(): type=dict(required=False, default=None, choices=['ethernet', 'team', 'team-slave', 'bond', 'bond-slave', 'bridge', 'bridge-slave', - 'vlan', 'generic'], + 'vlan', 'vxlan', 'generic'], type='str'), ip4=dict(required=False, default=None, type='str'), gw4=dict(required=False, default=None, type='str'), @@ -1209,6 +1283,10 @@ def main(): flags=dict(required=False, default=None, type='str'), ingress=dict(required=False, default=None, type='str'), egress=dict(required=False, default=None, type='str'), + # vxlan specific vars + vxlan_id=dict(required=False, default=None, type='str'), + vxlan_local=dict(required=False, default=None, type='str'), + vxlan_remote=dict(required=False, default=None, type='str'), ), supports_check_mode=True ) diff --git a/test/units/modules/net_tools/test_nmcli.py b/test/units/modules/net_tools/test_nmcli.py index 86713226d14..8719a30fda6 100644 --- a/test/units/modules/net_tools/test_nmcli.py +++ b/test/units/modules/net_tools/test_nmcli.py @@ -52,6 +52,12 @@ TESTCASE_CONNECTION = [ 'state': 'absent', '_ansible_check_mode': True, }, + { + 'type': 'vxlan', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, ] TESTCASE_GENERIC = [ @@ -131,6 +137,18 @@ TESTCASE_VLAN = [ } ] +TESTCASE_VXLAN = [ + { + 'type': 'vxlan', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'vxlan-existent_nw_device', + 'vxlan_id': '11', + 'vxlan_local': '192.168.225.5', + 'vxlan_remote': '192.168.225.6', + 'state': 'present', + '_ansible_check_mode': False, + } +] TESTCASE_ETHERNET_DHCP = [ { @@ -436,6 +454,52 @@ def test_mod_vlan_conn(mocked_generic_connection_modify): assert param in args[0] +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VXLAN, indirect=['patch_ansible_module']) +def test_create_vxlan(mocked_generic_connection_create): + """ + Test if vxlan created + """ + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 1 + arg_list = nmcli.Nmcli.execute_command.call_args_list + args, kwargs = arg_list[0] + + assert args[0][0] == '/usr/bin/nmcli' + assert args[0][1] == 'con' + assert args[0][2] == 'add' + assert args[0][3] == 'type' + assert args[0][4] == 'vxlan' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + assert args[0][7] == 'ifname' + + for param in ['vxlan.local', '192.168.225.5', 'vxlan.remote', '192.168.225.6', 'vxlan.id', '11']: + assert param in args[0] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VXLAN, indirect=['patch_ansible_module']) +def test_vxlan_mod(mocked_generic_connection_modify): + """ + Test if vxlan modified + """ + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 1 + arg_list = nmcli.Nmcli.execute_command.call_args_list + args, kwargs = arg_list[0] + + 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 ['vxlan.local', '192.168.225.5', 'vxlan.remote', '192.168.225.6', 'vxlan.id', '11']: + assert param in args[0] + + @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): """