diff --git a/lib/ansible/module_utils/network/nxos/nxos.py b/lib/ansible/module_utils/network/nxos/nxos.py index b8025d68e7c..a60138dcb40 100644 --- a/lib/ansible/module_utils/network/nxos/nxos.py +++ b/lib/ansible/module_utils/network/nxos/nxos.py @@ -123,13 +123,17 @@ class Cli: """ flags = [] if flags is None else flags - if self._device_configs != {}: - return self._device_configs - else: + cmd = 'show running-config ' + cmd += ' '.join(flags) + cmd = cmd.strip() + + try: + return self._device_configs[cmd] + except KeyError: connection = self._get_connection() out = connection.get_config(flags=flags) cfg = to_text(out, errors='surrogate_then_replace').strip() - self._device_configs = cfg + self._device_configs[cmd] = cfg return cfg def run_commands(self, commands, check_rc=True): diff --git a/lib/ansible/modules/network/nxos/nxos_vlan.py b/lib/ansible/modules/network/nxos/nxos_vlan.py index f75c4455531..db3511ecc55 100644 --- a/lib/ansible/modules/network/nxos/nxos_vlan.py +++ b/lib/ansible/modules/network/nxos/nxos_vlan.py @@ -45,6 +45,10 @@ options: - Name of VLAN. required: false default: null + interfaces: + description: + - List of interfaces that should be associated to the VLAN. + version_added: "2.5" vlan_state: description: - Manage the vlan operational state of the VLAN @@ -62,7 +66,7 @@ options: mapped_vni: description: - The Virtual Network Identifier (VNI) ID that is mapped to the - VLAN. Valid values are integer and keyword 'default'. + VLAN. Valid values are integer and keyword 'default'. Range 4096-16773119. required: false default: null version_added: "2.2" @@ -76,10 +80,14 @@ options: mode: description: - Set VLAN mode to classical ethernet or fabricpath. + This is a valid option for Nexus 5000 and 7000 series. required: false - default: null + default: ce choices: ['ce','fabricpath'] version_added: "2.4" + aggregate: + description: List of VLANs definitions. + version_added: "2.5" ''' EXAMPLES = ''' @@ -87,20 +95,30 @@ EXAMPLES = ''' nxos_vlan: vlan_range: "2-10,20,50,55-60,100-150" state: absent - transport: nxapi - name: Ensure VLAN 50 exists with the name WEB and is in the shutdown state nxos_vlan: vlan_id: 50 admin_state: down name: WEB - transport: nxapi - name: Ensure VLAN is NOT on the device nxos_vlan: vlan_id: 50 state: absent - transport: nxapi + +- name: Add interfaces to VLAN + nxos_vlan: + vlan_id: 100 + interfaces: + - Ethernet2/1 + - Ethernet2/5 + +- name: Create aggregate of vlans + nxos_vlan: + aggregate: + - { vlan_id: 4000, mode: ce } + - { vlan_id: 4001, name: vlan-4001 } ''' RETURN = ''' @@ -112,270 +130,397 @@ commands: ''' import re +import time + +from copy import deepcopy from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands -from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args +from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.common.utils import remove_default_spec + + +def search_obj_in_list(vlan_id, lst): + for o in lst: + if o['vlan_id'] == vlan_id: + return o + + +def get_diff(w, have): + del w['interfaces'] + del w['name'] + for o in have: + del o['interfaces'] + del o['name'] + if o['vlan_id'] == w['vlan_id']: + diff_dict = dict(set(w.items()) - set(o.items())) + return diff_dict + + +def map_obj_to_commands(updates, module, os_platform): + commands = list() + want, have = updates + + for w in want: + vlan_id = w['vlan_id'] + name = w['name'] + interfaces = w.get('interfaces') or [] + mapped_vni = w['mapped_vni'] + vlan_state = w['vlan_state'] + admin_state = w['admin_state'] + state = w['state'] + del w['state'] + if any(i in os_platform for i in ['5K', '7K']): + mode = w['mode'] + else: + w['mode'] = None + mode = w['mode'] + obj_in_have = search_obj_in_list(vlan_id, have) + + if state == 'absent': + if obj_in_have: + if obj_in_have['mapped_vni'] != 'None': + commands.append('vlan {0}'.format(vlan_id)) + commands.append('no vn-segment') + commands.append('exit') + commands.append('no vlan {0}'.format(vlan_id)) + + elif state == 'present': + if not obj_in_have: + commands.append('vlan {0}'.format(vlan_id)) + + if name: + commands.append('name {0}'.format(name)) + if mode: + commands.append('mode {0}'.format(mode)) + if vlan_state: + commands.append('state {0}'.format(vlan_state)) + if mapped_vni != 'None': + commands.append('vn-segment {0}'.format(mapped_vni)) + if admin_state == 'up': + commands.append('no shutdown') + if admin_state == 'down': + commands.append('shutdown') + commands.append('exit') + + if interfaces: + for i in interfaces: + commands.append('interface {0}'.format(i)) + commands.append('switchport') + commands.append('switchport mode access') + commands.append('switchport access vlan {0}'.format(vlan_id)) -def vlan_range_to_list(vlans): - result = [] - if vlans: - for part in vlans.split(','): - if part == 'none': - break - if '-' in part: - start, end = part.split('-') - start, end = int(start), int(end) - result.extend([str(i) for i in range(start, end + 1)]) else: - result.append(part) - return result + if interfaces: + if not obj_in_have['interfaces']: + for i in interfaces: + commands.append('vlan {0}'.format(vlan_id)) + commands.append('exit') + commands.append('interface {0}'.format(i)) + commands.append('switchport') + commands.append('switchport mode access') + commands.append('switchport access vlan {0}'.format(vlan_id)) + + elif set(interfaces) != set(obj_in_have['interfaces']): + missing_interfaces = list(set(interfaces) - set(obj_in_have['interfaces'])) + for i in missing_interfaces: + commands.append('vlan {0}'.format(vlan_id)) + commands.append('exit') + commands.append('interface {0}'.format(i)) + commands.append('switchport') + commands.append('switchport mode access') + commands.append('switchport access vlan {0}'.format(vlan_id)) + + superfluous_interfaces = list(set(obj_in_have['interfaces']) - set(interfaces)) + for i in superfluous_interfaces: + commands.append('vlan {0}'.format(vlan_id)) + commands.append('exit') + commands.append('interface {0}'.format(i)) + commands.append('switchport') + commands.append('switchport mode access') + commands.append('no switchport access vlan {0}'.format(vlan_id)) + else: + diff = get_diff(w, have) + if diff: + commands.append('vlan {0}'.format(vlan_id)) + for key, value in diff.items(): + if key == 'vlan_state': + commands.append('state {0}'.format(value)) + if key == 'mapped_vni': + if value != 'None': + commands.append('vn-segment {0}'.format(value)) + if key == 'admin_state': + if value == 'up': + commands.append('no shutdown') + elif value == 'down': + commands.append('shutdown') + if key == 'mode': + commands.append('mode {0}'.format(value)) + commands.append('exit') -def numerical_sort(iterable): - """Sort list of strings (VLAN IDs) that are digits in numerical order. - """ - as_int_list = [] - for vlan in iterable: - as_int_list.append(int(vlan)) - as_int_list.sort() - - as_str_list = [] - for vlan in as_int_list: - as_str_list.append(str(vlan)) - return as_str_list - - -def build_commands(vlans, state): - commands = [] - for vlan in vlans: - if state == 'present': - command = 'vlan {0}'.format(vlan) - commands.append(command) - elif state == 'absent': - command = 'no vlan {0}'.format(vlan) - commands.append(command) return commands -def get_vlan_config_commands(vlan, vid): - """Build command list required for VLAN configuration - """ - - reverse_value_map = { - "admin_state": { - "down": "shutdown", - "up": "no shutdown" - } - } - - if vlan.get('admin_state'): - # apply value map when making change to the admin state - # note: would need to be a loop or more in depth check if - # value map has more than 1 key - vlan = apply_value_map(reverse_value_map, vlan) - - vlan_args = { - 'name': 'name {0}', - 'vlan_state': 'state {0}', - 'admin_state': '{0}', - 'mode': 'mode {0}', - 'mapped_vni': 'vn-segment {0}' - } - - commands = [] - - for param, value in vlan.items(): - if param == 'mapped_vni' and value == 'default': - command = 'no vn-segment' +def want_vlan_list(module): + result = [] + vlan_range = module.params['vlan_range'] + for part in vlan_range.split(','): + if part == 'none': + break + if '-' in part: + start, end = part.split('-') + start, end = int(start), int(end) + result.extend([str(i) for i in range(start, end + 1)]) else: - command = vlan_args.get(param).format(vlan.get(param)) - if command: - commands.append(command) + result.append(part) + return result + + +def have_vlan_list(have): + result = [] + if have: + for h in have: + result.append(str(h.get('vlan_id'))) + return result + - commands.insert(0, 'vlan ' + vid) - commands.append('exit') +def vlan_range_commands(module, have): + commands = list() + proposed_vlans_list = want_vlan_list(module) + existing_vlans_list = have_vlan_list(have) + + if module.params['state'] == 'absent': + vlans = set(proposed_vlans_list).intersection(existing_vlans_list) + for vlan in vlans: + commands.append('no vlan {0}'.format(vlan)) + + elif module.params['state'] == 'present': + vlans = set(proposed_vlans_list).difference(existing_vlans_list) + for vlan in vlans: + commands.append('vlan {0}'.format(vlan)) return commands -def get_list_of_vlans(module): - body = run_commands(module, ['show vlan | json'])[0] - vlan_list = [] - vlan_table = body.get('TABLE_vlanbrief')['ROW_vlanbrief'] +def map_params_to_obj(module): + obj = [] + if module.params['vlan_range']: + return [] + + aggregate = module.params.get('aggregate') + if aggregate: + for item in aggregate: + for key in item: + if item.get(key) is None: + item[key] = module.params[key] + + d = item.copy() + d['vlan_id'] = str(d['vlan_id']) + d['mapped_vni'] = str(d['mapped_vni']) - if isinstance(vlan_table, list): - for vlan in vlan_table: - vlan_list.append(str(vlan['vlanshowbr-vlanid-utf'])) + obj.append(d) else: - vlan_list.append('1') + obj.append({ + 'vlan_id': str(module.params['vlan_id']), + 'name': module.params['name'], + 'interfaces': module.params['interfaces'], + 'vlan_state': module.params['vlan_state'], + 'mapped_vni': str(module.params['mapped_vni']), + 'state': module.params['state'], + 'admin_state': module.params['admin_state'], + 'mode': module.params['mode'] + }) - return vlan_list + return obj -def get_vni(vlanid, module): - flags = str('all | section vlan.{0}'.format(vlanid)).split(' ') - body = get_config(module, flags=flags) +def parse_admin_state(vlan): + shutstate = vlan.get('vlanshowbr-shutstate') + if shutstate == 'noshutdown': + return 'up' + elif shutstate == 'shutdown': + return 'down' - value = '' - if body: - REGEX = re.compile(r'(?:vn-segment\s)(?P.*)$', re.M) - if 'vn-segment' in body: - value = REGEX.search(body).group('value') - return value +def parse_mode(os_platform, output, vlan_id): + if not any(i in os_platform for i in ['5K', '7K']): + return None -def get_vlan(vlanid, module): - """Get instance of VLAN as a dictionary - """ - command = 'show vlan id %s | json' % vlanid try: - body = run_commands(module, [command])[0] - vlan_table = body['TABLE_vlanbriefid']['ROW_vlanbriefid'] - mtu_table = body['TABLE_mtuinfoid']['ROW_mtuinfoid'] - except (TypeError, IndexError, KeyError): - return {} + mtus = output['TABLE_mtuinfo']['ROW_mtuinfo'] + except KeyError: + return None + + if mtus: + if isinstance(mtus, list): + for mtu in mtus: + if mtu['vlanshowinfo-vlanid'] == vlan_id: + mode = mtu.get('vlanshowinfo-vlanmode') + if mode == 'ce-vlan': + return 'ce' + elif mode == 'fabricpath-vlan': + return 'fabricpath' + return None + + elif isinstance(mtus, dict): + if mtus['vlanshowinfo-vlanid'] == vlan_id: + mode = mtus.get('vlanshowinfo-vlanmode') + if mode == 'ce-vlan': + return 'ce' + elif mode == 'fabricpath-vlan': + return 'fabricpath' + return None + + else: + return None + else: + return None + + +def parse_vni(module, vlan_id): + vni = None + flags = ['| section vlan.{0}'.format(vlan_id)] + cfg = get_config(module, flags=flags) + + match = re.search(r'vn-segment (\S+)', cfg, re.M) + if match: + vni = match.group(1) + return str(vni) + + +def parse_interfaces(module, vlan): + vlan_int = [] + interfaces = vlan.get('vlanshowplist-ifidx') + if interfaces: + for i in interfaces.split(','): + if '-' in i: + int_range = i.split('-') + stop = int((int_range)[1]) + start = int(int_range[0].split('/')[1]) + eth = int_range[0].split('/')[0] + for r in range(start, stop + 1): + vlan_int.append(eth + '/' + str(r)) + else: + vlan_int.append(i) + + return vlan_int - key_map = { - "vlanshowbr-vlanid-utf": "vlan_id", - "vlanshowbr-vlanname": "name", - "vlanshowbr-vlanstate": "vlan_state", - "vlanshowbr-shutstate": "admin_state" - } - vlan = apply_key_map(key_map, vlan_table) +def parse_vlan_options(module, os_platform, output, vlan): + obj = {} + vlan_id = vlan['vlanshowbr-vlanid-utf'] + obj['vlan_id'] = str(vlan_id) + obj['name'] = vlan.get('vlanshowbr-vlanname') + obj['vlan_state'] = vlan.get('vlanshowbr-vlanstate') + obj['admin_state'] = parse_admin_state(vlan) + obj['mode'] = parse_mode(os_platform, output, vlan_id) + obj['mapped_vni'] = parse_vni(module, vlan_id) + obj['interfaces'] = parse_interfaces(module, vlan) + return obj - vlan['mode'] = mtu_table['vlanshowinfo-vlanmode'] - value_map = { - "admin_state": { - "shutdown": "down", - "noshutdown": "up" - }, - "mode": { - "fabricpath-vlan": "fabricpath", - "ce-vlan": "ce" - } +def map_config_to_obj(module, os_platform): + objs = list() + output = run_commands(module, ['show vlan | json'])[0] + try: + vlans = output['TABLE_vlanbrief']['ROW_vlanbrief'] + except KeyError: + return objs - } + if vlans: + if isinstance(vlans, list): + for vlan in vlans: + obj = parse_vlan_options(module, os_platform, output, vlan) + objs.append(obj) - vlan = apply_value_map(value_map, vlan) - vlan['mapped_vni'] = get_vni(vlanid, module) - return vlan + elif isinstance(vlans, dict): + obj = parse_vlan_options(module, os_platform, output, vlans) + objs.append(obj) + return objs -def apply_key_map(key_map, table): - new_dict = {} - for key, value in table.items(): - new_key = key_map.get(key) - if new_key: - new_dict[new_key] = str(value) - return new_dict +def check_declarative_intent_params(want, module, os_platform): + if module.params['interfaces']: + time.sleep(module.params['delay']) + have = map_config_to_obj(module, os_platform) -def apply_value_map(value_map, resource): - for key, value in value_map.items(): - resource[key] = value[resource.get(key)] - return resource + for w in want: + for i in w['interfaces']: + obj_in_have = search_obj_in_list(w['vlan_id'], have) + if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']: + module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id'])) def main(): - argument_spec = dict( - vlan_id=dict(required=False, type='str'), + """ main entry point for module execution + """ + element_spec = dict( + vlan_id=dict(required=False, type='int'), vlan_range=dict(required=False), name=dict(required=False), - vlan_state=dict(choices=['active', 'suspend'], required=False), - mapped_vni=dict(required=False, type='str'), + interfaces=dict(type='list'), + vlan_state=dict(choices=['active', 'suspend'], required=False, default='active'), + mapped_vni=dict(required=False, type='int'), + delay=dict(default=10, type='int'), state=dict(choices=['present', 'absent', 'active', 'suspend'], default='present', required=False), - admin_state=dict(choices=['up', 'down'], required=False), - mode=dict(choices=['ce', 'fabricpath'], required=False), + admin_state=dict(choices=['up', 'down'], required=False, default='up'), + mode=dict(choices=['ce', 'fabricpath'], required=False, default='ce'), + ) + + aggregate_spec = deepcopy(element_spec) + aggregate_spec['vlan_id'] = dict(required=True) + # remove default in aggregate spec, to handle common arguments + remove_default_spec(aggregate_spec) + + argument_spec = dict( + aggregate=dict(type='list', elements='dict', options=aggregate_spec), + purge=dict(default=False, type='bool') ) + argument_spec.update(element_spec) argument_spec.update(nxos_argument_spec) + required_one_of = [['vlan_id', 'aggregate', 'vlan_range']] + mutually_exclusive = [['vlan_id', 'aggregate'], + ['vlan_range', 'name'], + ['vlan_id', 'vlan_range']] + module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=[['vlan_range', 'name'], - ['vlan_id', 'vlan_range']], + required_one_of=required_one_of, + mutually_exclusive=mutually_exclusive, supports_check_mode=True) + info = get_capabilities(module).get('device_info', {}) + os_platform = info.get('network_os_platform', '') + warnings = list() - check_args(module, warnings) - results = dict(changed=False) + result = {'changed': False} + if warnings: + result['warnings'] = warnings - vlan_range = module.params['vlan_range'] - vlan_id = module.params['vlan_id'] - name = module.params['name'] - vlan_state = module.params['vlan_state'] - admin_state = module.params['admin_state'] - mapped_vni = module.params['mapped_vni'] - state = module.params['state'] - - # this allows vlan_state to remain backwards compatible as we move towards - # pushing all 4 options into state to match net_vlan - if state == 'active' or state == 'suspend': - vlan_state = module.params['state'] - state = 'present' - - mode = module.params['mode'] - - if vlan_id: - if not vlan_id.isdigit(): - module.fail_json(msg='vlan_id must be a valid VLAN ID') - - args = dict(name=name, vlan_state=vlan_state, - admin_state=admin_state, mapped_vni=mapped_vni, mode=mode) - - proposed = dict((k, v) for k, v in args.items() if v is not None) - - proposed_vlans_list = vlan_range_to_list(vlan_id or vlan_range) - existing_vlans_list = get_list_of_vlans(module) - commands = [] - existing = {} - - if vlan_range: - if state == 'present': - # These are all of the VLANs being proposed that don't - # already exist on the switch - vlans_delta = numerical_sort( - set(proposed_vlans_list).difference(existing_vlans_list)) - commands = build_commands(vlans_delta, state) - elif state == 'absent': - # VLANs that are common between what is being proposed and - # what is on the switch - vlans_common = numerical_sort( - set(proposed_vlans_list).intersection(existing_vlans_list)) - commands = build_commands(vlans_common, state) + have = map_config_to_obj(module, os_platform) + want = map_params_to_obj(module) + + if module.params['vlan_range']: + commands = vlan_range_commands(module, have) + result['commands'] = commands else: - existing = get_vlan(vlan_id, module) - if state == 'absent' and existing: - commands = ['no vlan ' + vlan_id] - elif state == 'present': - if (existing.get('mapped_vni') == '0' and - proposed.get('mapped_vni') == 'default'): - proposed.pop('mapped_vni') - delta = dict(set(proposed.items()).difference(existing.items())) - if delta or not existing: - commands = get_vlan_config_commands(delta, vlan_id) + commands = map_obj_to_commands((want, have), module, os_platform) + result['commands'] = commands if commands: - if existing.get('mapped_vni'): - if (existing.get('mapped_vni') != proposed.get('mapped_vni') and - existing.get('mapped_vni') != '0' and proposed.get('mapped_vni') != 'default'): - if state == 'absent': - commands = ['vlan ' + vlan_id, 'no vn-segment', 'no vlan ' + vlan_id] - else: - commands.insert(1, 'no vn-segment') - if module.check_mode: - module.exit_json(changed=True, commands=commands) - else: + if not module.check_mode: load_config(module, commands) - results['changed'] = True + result['changed'] = True - results['commands'] = commands - module.exit_json(**results) + if want and result['changed']: + check_declarative_intent_params(want, module, os_platform) + module.exit_json(**result) if __name__ == '__main__': main() diff --git a/test/integration/targets/nxos_vlan/tests/common/interface.yaml b/test/integration/targets/nxos_vlan/tests/common/interface.yaml new file mode 100644 index 00000000000..7adc108b8ad --- /dev/null +++ b/test/integration/targets/nxos_vlan/tests/common/interface.yaml @@ -0,0 +1,105 @@ +--- +- set_fact: testint1="{{ nxos_int1 }}" +- set_fact: testint2="{{ nxos_int2 }}" + +- name: setup - remove vlan used in test + nxos_config: + lines: + - no vlan 100 + provider: "{{ connection }}" + +- name: setup - remove vlan from interfaces used in test(part1) + nxos_config: + lines: + - no switchport access vlan 100 + parents: switchport + before: "interface {{ testint1 }}" + provider: "{{ connection }}" + +- name: setup - remove vlan from interfaces used in test(part2) + nxos_config: + lines: + - no switchport access vlan 100 + parents: switchport + before: "interface {{ testint2 }}" + provider: "{{ connection }}" + +- name: create vlan + nxos_vlan: + vlan_id: 100 + provider: "{{ connection }}" + +- name: Add interfaces to vlan + nxos_vlan: &interfaces + vlan_id: 100 + interfaces: + - "{{ testint1 }}" + - "{{ testint2 }}" + provider: "{{ connection }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface {{ testint1 }}" in result.commands' + - '"switchport" in result.commands' + - '"switchport mode access" in result.commands' + - '"switchport access vlan 100" in result.commands' + - '"interface {{ testint2 }}" in result.commands' + - '"switchport" in result.commands' + - '"switchport mode access" in result.commands' + - '"switchport access vlan 100" in result.commands' + +- name: Add interfaces to vlan(idempotence) + nxos_vlan: *interfaces + register: result + +- assert: + that: + - 'result.changed == false' + +- name: Remove interface from vlan + nxos_vlan: &single_int + vlan_id: 100 + interfaces: + - "{{ testint2 }}" + provider: "{{ connection }}" + register: result + +- assert: + that: + - 'result.changed == true' + - '"interface {{ testint1 }}" in result.commands' + - '"switchport" in result.commands' + - '"switchport mode access" in result.commands' + - '"no switchport access vlan 100" in result.commands' + +- name: Remove interface from vlan(idempotence) + nxos_vlan: *single_int + register: result + +- assert: + that: + - 'result.changed == false' + +- name: teardown(part1) + nxos_config: + lines: + - no vlan 100 + provider: "{{ connection }}" + +- name: teardown - remove vlan from interfaces used in test(part1) + nxos_config: + lines: + - no switchport access vlan 100 + parents: switchport + before: "interface {{ testint1 }}" + provider: "{{ connection }}" + +- name: teardown - remove vlan from interfaces used in test(part2) + nxos_config: + lines: + - no switchport access vlan 100 + parents: switchport + before: "interface {{ testint2 }}" + provider: "{{ connection }}" diff --git a/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_id_15.txt b/test/units/modules/network/nxos/fixtures/nxos_vlan/config.cfg similarity index 100% rename from test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_id_15.txt rename to test/units/modules/network/nxos/fixtures/nxos_vlan/config.cfg diff --git a/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_id_1.txt b/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_id_1.txt deleted file mode 100644 index e919faaeac7..00000000000 --- a/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_id_1.txt +++ /dev/null @@ -1,20 +0,0 @@ -{ - "TABLE_vlanbriefid": { - "ROW_vlanbriefid": { - "vlanshowbr-vlanid": 16777216, - "vlanshowbr-vlanid-utf": 1, - "vlanshowbr-vlanname": "default", - "vlanshowbr-vlanstate": "active", - "vlanshowbr-shutstate": "noshutdown" - } - }, - "TABLE_mtuinfoid": { - "ROW_mtuinfoid": { - "vlanshowinfo-vlanid": 1, - "vlanshowinfo-media-type": "enet", - "vlanshowinfo-vlanmode": "ce-vlan" - } - }, - "vlanshowrspan-vlantype": "notrspan", - "is-vtp-manageable": "enabled" -} diff --git a/test/units/modules/network/nxos/test_nxos_vlan.py b/test/units/modules/network/nxos/test_nxos_vlan.py index 16806407f97..ba2469eebdb 100644 --- a/test/units/modules/network/nxos/test_nxos_vlan.py +++ b/test/units/modules/network/nxos/test_nxos_vlan.py @@ -42,10 +42,16 @@ class TestNxosVlanModule(TestNxosModule): self.mock_get_config = patch('ansible.modules.network.nxos.nxos_vlan.get_config') self.get_config = self.mock_get_config.start() + self.mock_get_capabilities = patch('ansible.modules.network.nxos.nxos_vlan.get_capabilities') + self.get_capabilities = self.mock_get_capabilities.start() + self.get_capabilities.return_value = {'network_api': 'cliconf'} + def tearDown(self): super(TestNxosVlanModule, self).tearDown() self.mock_run_commands.stop() self.mock_load_config.stop() + self.mock_get_config.stop() + self.mock_get_capabilities.stop() def load_fixtures(self, commands=None, device=''): def load_from_file(*args, **kwargs): @@ -64,11 +70,11 @@ class TestNxosVlanModule(TestNxosModule): self.run_commands.side_effect = load_from_file self.load_config.return_value = None + self.get_config.return_value = load_fixture('nxos_vlan', 'config.cfg') def test_nxos_vlan_range(self): set_module_args(dict(vlan_range='6-10')) - result = self.execute_module(changed=True) - self.assertEqual(result['commands'], ['vlan 6', 'vlan 7', 'vlan 8', 'vlan 9', 'vlan 10']) + self.execute_module(changed=True, commands=['vlan 6', 'vlan 7', 'vlan 8', 'vlan 9', 'vlan 10']) def test_nxos_vlan_range_absent(self): set_module_args(dict(vlan_range='1-5', state='absent')) @@ -78,7 +84,7 @@ class TestNxosVlanModule(TestNxosModule): def test_nxos_vlan_id(self): set_module_args(dict(vlan_id='15', state='present')) result = self.execute_module(changed=True) - self.assertEqual(result['commands'], ['vlan 15', 'exit']) + self.assertEqual(result['commands'], ['vlan 15', 'state active', 'no shutdown', 'exit']) def test_nxos_vlan_id_absent(self): set_module_args(dict(vlan_id='1', state='absent')) @@ -88,7 +94,7 @@ class TestNxosVlanModule(TestNxosModule): def test_nxos_vlan_named_vlan(self): set_module_args(dict(vlan_id='15', name='WEB')) result = self.execute_module(changed=True) - self.assertEqual(result['commands'], ['vlan 15', 'name WEB', 'exit']) + self.assertEqual(result['commands'], ['vlan 15', 'name WEB', 'state active', 'no shutdown', 'exit']) def test_nxos_vlan_shut_down(self): set_module_args(dict(vlan_id='1', admin_state='down'))