Repository: ansible/ansible
Pull request: nxos_vlan refactor to support non structured output
Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

##### SUMMARY
<!--- Describe the change, including rationale and design decisions -->
fixes https://github.com/ansible/ansible/issues/43481
fixes https://github.com/ansible/ansible/issues/40705
fixes https://github.com/ansible/ansible/issues/40236
fixes https://github.com/ansible/ansible/issues/43627

- Refactor nxos_vlan module to support non structured output
- Introduces new option `retry_json` to check_rc  in `run_commands` in module_utils -> Sends text command if JSON command fails for cliconf only
<!--- If you are fixing an existing issue, please include "Fixes #nnn" in your
commit message and your description; but you should still explain what
the change does.-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bugfix Pull Request

##### COMPONENT NAME
<!--- Name of the module, plugin, task or feature -->
modules/network/nxos/nxos_vlan.py
modules/network/nxos/nxos_facts.py
module_utils/network/nxos/nxos.py
plugins/{cliconf,httpapi}/nxos.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
devel
```


Problem statement:
nxos_vlan mode issue on N5k
<!---
nxos_vlan mode fabricpath issue on n5k
--->

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
nxos_vlan
##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
2.5.2
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->
default from pip
##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
centos6 and nxos 7.3 on nexus 5500.
##### SUMMARY
<!--- Explain the problem briefly -->
nxos_vlan mode fabricpath issue on n5k with nxapi
I can see in the module about network_os_platform for option mode. 
##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: add vlan in pod
  hosts: ix1-test
  connection: local
  any_errors_fatal: true
  gather_facts: false
  vars:
    ansible_user: "{{ tower_user_name }}"
    ansible_password: "{{ password }}"
    nxapi:
      username: "{{ tower_user_name }}"
      password: "{{ password }}"
      transport: nxapi
      use_ssl: yes
      validate_certs: no
      port: 10443
      timeout: 15
      host: "{{ inventory_hostname }}"
  tasks:
    - name: create vlan in switches
      nxos_vlan:
        vlan_id: "{{ vlan_id }}"
        state: present
        name: "{{ vlan_name }}"
        admin_state: up
        mode: fabricpath
        provider: "{{ nxapi }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
```
# from switch
#sh vlan id 16

VLAN Name                             Status    Ports
---- -------------------------------- --------- -------------------------------
16   ABC-10.10.16.0_25                active    Po1, Po11, Po34, Eth1/4
                                                Eth1/32

VLAN Type  Vlan-mode
---- ----- ----------
16   enet  FABRICPATH

Primary  Secondary  Type             Ports
-------  ---------  ---------------  -------------------------------------------

```
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
PLAY [add vlan in pod] **************************************************************************************************************************

TASK [create vlan in switches] ******************************************************************************************************************
changed: [ix1-test-s101a.net.dcinf.se] => {"changed": true, "commands": ["vlan 16", "name ABC-10.10.16.0_25", "state active", "no shutdown", "exit"]

# from switch
# sh vlan id 16

VLAN Name                             Status    Ports
---- -------------------------------- --------- -------------------------------
16   ABC-10.10.16.0_25                active    Po1, Po11, Po34, Eth1/4
                                                Eth1/32

VLAN Type  Vlan-mode
---- ----- ----------
16   enet  CE

Primary  Secondary  Type             Ports
-------  ---------  ---------------  -------------------------------------------

```


nxos_vlan - failed to create VLAN in N5K without FabricPath
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
nxos_vlan

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/srv/home/guirlinger_j/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/rh/rh-python36/root/usr/lib/python3.6/site-packages/ansible
  executable location = /opt/rh/rh-python36/root/usr/bin/ansible
  python version = 3.6.3 (default, Jan  9 2018, 10:19:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]

```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->
Default
##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->

Ansible run from : Red Hat Enterprise Linux Server release 7.4 (Maipo)
Target device : cisco Nexus 5696 runnning NXOS 7.2(1)N1(1) **with no FabricPath feature**

##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->

<!--- Paste example playbooks or commands between quotes below -->
Simple playbook for creating VLANs on Nexus 5596 switches

```yaml
---
- hosts: nexus5596-lab
  gather_facts: False

vars:
    VLANs :
      - { vlan_id: 1, name: "default" }
      - { vlan_id: 3504, name: "test_ansible1" }
      - { vlan_id: 3505, name: "test_ansible2" }
      - { vlan_id: 3506, name: "test_ansible3" }  

  tasks:
    - name: create VLANs on lab Switches
      nxos_vlan:
        aggregate:
          "{{ VLANs }}"
        purge: yes
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
VLANs should be created on switches

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
The playbook fails on first attempt, because nxos_vlan try to pass the command *mode ce* under the vlan
<!--- Paste verbatim command output between quotes below -->

```
TASK [create VLANs on lab Switches] **********************************************************************************************************************************************************************************************
fatal: [labswitch2]: FAILED! => {"changed": false, "msg": "mode ce\r\r\n                                 ^\r\n% Invalid command at '^' marker.\r\n\rlabswitch2(config-vlan)# "}
fatal: [labswitch1]: FAILED! => {"changed": false, "msg": "mode ce\r\r\n                                 ^\r\n% Invalid command at '^' marker.\r\n\rlabswitch1(config-vlan)# "}

```

Only the first VLAN is created and then the play fails because of the commande "mode ce" which is not supported.

The problem is that nxos_vlan only check if the plateform is either 5k or 7k to know if the command *mode ce* is supported

```python
    if any(i in os_platform for i in ['5K', '7K']):
        mode = w['mode']
    else:
        w['mode'] = None
```

But this is not sufficient, as far as i know, you must have the feature-set fabricpath installed

IMO, there is no need to execute *mode ce* on VLAN creation, especially when this value is not specified in the playbook.
CE is the default mode on Cisco switches when creating a VLAN



Module nxos_vlan is not working with nexus 5k device
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
The issue is with the modules net_vlan and nxos_vlan which are not compatible with Cisco Nexus 5k devices.

##### ISSUE TYPE
 - Bug Report
##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
nxos_vlan

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible-playbook 2.7.0.dev0 (devel 1305fca414) last updated 2018/07/25 13:01:53 (GMT +000)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/USER.ET/ansible_devel/ansible/lib/ansible
  executable location = /home/USER.ET/ansible_devel/ansible/bin/ansible-playbook
  python version = 2.7.5 (default, May  3 2017, 07:55:04) [GCC 4.8.5 20150623 (Red Hat 4.8.5-14)]
```

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
It should work without any error

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
ansible-playbook 2.7.0.dev0 (devel 1305fca414) last updated 2018/07/25 13:01:53 (GMT +000)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/USER.ET/ansible_devel/ansible/lib/ansible
  executable location = /home/USER.ET/ansible_devel/ansible/bin/ansible-playbook
  python version = 2.7.5 (default, May  3 2017, 07:55:04) [GCC 4.8.5 20150623 (Red Hat 4.8.5-14)]
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
Parsed HOST, inventory source with host_list plugin
Loading callback plugin default of type stdout, v2.0 from /home/USER.ET/ansible_devel/ansible/lib/ansible/plugins/callback/default.pyc

PLAYBOOK: add_vlan.yml ****************************************************************************************************************
1 plays in add_vlan.yml

PLAY [add VLAN] ***********************************************************************************************************************
META: ran handlers

TASK [update VLANS] *******************************************************************************************************************
task path: /var/lib/awx/projects/VLAN/add_vlan.yml:11
<HOST> attempting to start connection
<HOST> using connection plugin network_cli
<HOST> local domain socket does not exist, starting it
<HOST> control socket path is /root/.ansible/pc/e12af63c4d
<HOST> <HOST> ESTABLISH CONNECTION FOR USER: USER on PORT 22 TO HOST
<HOST> <HOST> ssh connection done, setting terminal
<HOST> <HOST> loaded terminal plugin for network_os nxos
<HOST> <HOST> loaded cliconf plugin for network_os nxos
<HOST> <HOST> firing event: on_open_shell()
<HOST> <HOST> ssh connection has completed successfully
<HOST> connection to remote device started successfully
<HOST> local domain socket listeners started successfully
<HOST> 
<HOST> local domain socket path is /root/.ansible/pc/e12af63c4d
Getting network OS from inventory
Running implementation module nxos_vlan
<HOST> ESTABLISH LOCAL CONNECTION FOR USER: root
<HOST> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-local-20430HNbWg0/ansible-tmp-1532949448.97-36234681371903 `" && echo ansible-tmp-1532949448.97-36234681371903="` echo /root/.ansible/tmp/ansible-local-20430HNbWg0/ansible-tmp-1532949448.97-36234681371903 `" ) && sleep 0'
Using module file /home/USER.ET/ansible_devel/ansible/lib/ansible/modules/network/nxos/nxos_vlan.py
<HOST> PUT /root/.ansible/tmp/ansible-local-20430HNbWg0/tmppDrdcr TO /root/.ansible/tmp/ansible-local-20430HNbWg0/ansible-tmp-1532949448.97-36234681371903/nxos_vlan.py
<HOST> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-local-20430HNbWg0/ansible-tmp-1532949448.97-36234681371903/ /root/.ansible/tmp/ansible-local-20430HNbWg0/ansible-tmp-1532949448.97-36234681371903/nxos_vlan.py && sleep 0'
<HOST> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-local-20430HNbWg0/ansible-tmp-1532949448.97-36234681371903/nxos_vlan.py && sleep 0'
Caching network OS nxos in facts
The full traceback is:
  File "/tmp/ansible_szRCl9/ansible_modlib.zip/ansible/module_utils/network/nxos/nxos.py", line 152, in run_commands
    return connection.run_commands(commands, check_rc)
  File "/tmp/ansible_szRCl9/ansible_modlib.zip/ansible/module_utils/connection.py", line 149, in __rpc__
    raise ConnectionError(to_text(msg, errors='surrogate_then_replace'), code=code)
fatal: [HOST]: FAILED! => {
    "ansible_facts": {
        "network_os": "nxos"
    }, 
    "changed": false, 
    "invocation": {
        "module_args": {
            "admin_state": "up", 
            "aggregate": null, 
            "associated_interfaces": null, 
            "auth_pass": null, 
            "authorize": null, 
            "delay": 10, 
            "host": null, 
            "interfaces": null, 
            "mapped_vni": null, 
            "mode": "ce", 
            "name": "devtest", 
            "password": null, 
            "port": null, 
            "provider": null, 
            "purge": false, 
            "ssh_keyfile": null, 
            "state": "present", 
            "timeout": null, 
            "transport": null, 
            "use_ssl": null, 
            "username": null, 
            "validate_certs": null, 
            "vlan_id": 23, 
            "vlan_range": null, 
            "vlan_state": "active"
        }
    }, 
    "msg": "show vlan | json\r\r\n                                ^\r\n% Invalid command at '^' marker.\r\n\rL3-UYK-LSC1-5KAD09# "
}
	to retry, use: --limit @/var/lib/awx/projects/VLAN/add_vlan.retry

PLAY RECAP ****************************************************************************************************************************
HOST               : ok=0    changed=0    unreachable=0    failed=1   


```


nxos_vlan parse_vni causing task to take hours instead of seconds
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
Issue is present with 2.6.2 and devel version of nxos_vlan

In a n5k device with a config of ~5k lines and ~1000 vlans, the parse_vni function uses get_config which causes the task to take forever since it is running it per vlan. 
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
nxos_vlan
##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.6.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/e600790/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.13 (default, Nov 24 2017, 17:33:09) [GCC 6.3.0 20170516]
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->
HOST_KEY_CHECKING(/etc/ansible/ansible.cfg) = False
RETRY_FILES_ENABLED(/etc/ansible/ansible.cfg) = False

##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
Debian 9.5 managing a nexus device running NXOS 7.2(1)
##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->
Compare output of "time ansible-playbook nxos_vlan.yml" and "time ansible-playbook nxos_vlan_custom.yml" where custom is using a modified nxos_vlan module without the parse_vni function
<!--- Paste example playbooks or commands between quotes below -->
```yaml
- nxos_vlan:
    vlan_id: "{{ vlan_id }}"
    mode: fabricpath
    state: present
    provider: "{{ nxapi }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
```
PLAY RECAP **********************************************************************************************************************************************************************************************************************************
n5k_hostname               : ok=1    changed=0    unreachable=0    failed=0


real    0m26,470s
user    0m3,712s
sys     0m0,620s
```
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
```
PLAY RECAP **********************************************************************************************************************************************************************************************************************************
n5k_hostname               : ok=1    changed=0    unreachable=0    failed=0


real    129m42,977s
user    8m9,940s
sys     1m32,464s
```

Hints from issue discussion:
Files identified in the description:
* [lib/ansible/modules/network/nxos/nxos_vlan.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/nxos/nxos_vlan.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jedelman8 @mikewiebe @privateip @rahushen @rcarrillocruz @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I see the same in ansible 2.5.3.
In general I think conditional in ansible module is not needed, like if platform x allow option fp
@hemskgren Can you please share `show inventory` output of your device?
test-s11a# show inventory
NAME: "Chassis", DESCR: "Nexus5548 Chassis"
PID: N5K-C5548UP       , VID: V01 , SN: SSI162208Z1

NAME: "Module 1", DESCR: "O2 32X10GE/Modular Universal Platform Supervisor"
PID: N5K-C5548UP       , VID: V01 , SN: FOC162616HM

NAME: "Module 2", DESCR: "O2 16 port flexible GEM"
PID: N55-M16UP         , VID: V01 , SN: FOC162456HP

NAME: "Fan 1", DESCR: "Chassis fan module"
PID: N5548P-FAN        , VID: N/A , SN: N/A

NAME: "Fan 2", DESCR: "Chassis fan module"
PID: N5548P-FAN        , VID: N/A , SN: N/A

NAME: "Power supply 1", DESCR: "AC power supply"
PID: N55-PAC-750W      , VID: V02 , SN: POG16307TT3

NAME: "Power supply 2", DESCR: "AC power supply"
PID: N55-PAC-750W      , VID: V02 , SN: POG16307TUS

NAME: "Module 3", DESCR: "O2 Non L3 Daughter Card"
PID: N55-DL2           , VID: V01 , SN: FOC163127NL

NAME: "FEX 102 CHASSIS", DESCR: "N2K-C2232PP-10GE  CHASSIS"
PID: N2K-C2232PP-10GE  , VID: V02 , SN: SSI14180C6X

NAME: "FEX 102 Module 1", DESCR: "Fabric Extender Module: 32x10GE, 8x10GE Superv
isor"
PID: N2K-C2232PP-10GE  , VID: V02 , SN: JAF1502BESP

NAME: "FEX 102 Fan 1", DESCR: "Fabric Extender Fan module"
PID: N2K-C2232-FAN     , VID: N/A , SN: N/A

NAME: "FEX 102 Power Supply 1", DESCR: "Fabric Extender AC power supply"
PID: N2200-PAC-400W    , VID: V02 , SN: LIT14350HK7

NAME: "FEX 102 Power Supply 2", DESCR: "Fabric Extender AC power supply"
PID: N2200-PAC-400W    , VID: V02 , SN: LIT14350HL4

test-s11a#
cc @tstoner
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/network/nxos/nxos_vlan.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/nxos/nxos_vlan.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jedelman8 @mikewiebe @privateip @rahushen @rcarrillocruz @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@jguirlinger Can you please share `show inventory` and `show vlan | json` output of your device?
here it is :
show inventory : https://gist.github.com/jguirlinger/f264ee0dd8d303fc2a6c9fb9c5b872b9
show vlan : https://gist.github.com/jguirlinger/fbc5a6a729c1ebdb08e3c3ec44a3c284

@jguirlinger I am not sure why `mode ce` is returning Invalid command on your device. 
From `show vlan` output it is clear that `ce-vlan` mode in compatible on nxos5K `"vlanshowinfo-vlanmode": "ce-vlan"`. 

What happens when you configure manually? I can make mode work on 7K. 
```
switch(config)# vlan 5
switch(config-vlan)# mode ?
  ce          Classical Ethernet VLAN mode
  fabricpath  Fabricpath VLAN mode
```
As the nxos doc says, it should work on 5K also https://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus5000/sw/fabricpath/513_n1_1/N5K_FabricPath_Configuration_Guide/fp_n5k_forwarding.html
The comand ``mode`` is not available

```
WPX-ARES-011(config-vlan)# vlan 3504
WPX-ARES-011(config-vlan)# ?
  name      Ascii name of the VLAN
  no        Negate a command or set its defaults
  shutdown  Shutdown VLAN switching
  state     Operational state of the VLAN
  end       Go to exec mode
  exit      Exit from command interpreter
  pop       Pop mode from stack or restore from name
  push      Push current mode to stack or save it under name
  where     Shows the cli context you are in
```

This is because of the FabricPath feature-set not installed
```
WPX-ARES-011(config-vlan)# show feature-set
Feature Set Name      ID        State
--------------------  --------  --------
fabricpath             2          uninstalled
```

When you do not have this feature activated, you can't change mode, and mode CE is the default

For me, it would be better if ansible have no default value for ``mode`` parameter
cc @tstoner
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/network/nxos/nxos_vlan.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/nxos/nxos_vlan.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jedelman8 @mikewiebe @privateip @rahushen @rcarrillocruz @trishnaguha @tstoner
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hi @pravindmali,

Thank you for the issue, just so you are aware we have a dedicated Working Group for network.
You can find other people interested in this in #ansible-network on Freenode IRC
For more information about communities, meetings and agendas see https://github.com/ansible/community

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: community_workgroups --->
resolved_by_pr https://github.com/ansible/ansible/pull/43805
Files identified in the description:
* [lib/ansible/modules/network/nxos/nxos_vlan.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/nxos/nxos_vlan.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jedelman8 @mikewiebe @privateip @rahushen @rcarrillocruz @trishnaguha @tstoner
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hi @denngie,

Thank you for the issue, just so you are aware we have a dedicated Working Group for network.
You can find other people interested in this in #ansible-network on Freenode IRC
For more information about communities, meetings and agendas see https://github.com/ansible/community

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: community_workgroups --->
Noticed it is using the flag '| section vlan.vlan_id', and when comparing via SSH on my device, it is much slower compared to 'show run vlan vlan_id', after modying the flag to use this syntax the task was quicker, but still unacceptable:
```
PLAY RECAP **********************************************************************************************************************************************************************************************************************************
n5k_hostname               : ok=1    changed=0    unreachable=0    failed=0


real    61m1,657s
user    3m47,856s
sys     0m48,276s
```

The only solution I can think is getting the configuration once, and then have parse_vni accept config as a input paremeter
Or maybe even better, only get config for the vni if the vni parameter was configured?

Commit message: address review comment

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

Patch to review:
diff --git a/lib/ansible/module_utils/network/nxos/nxos.py b/lib/ansible/module_utils/network/nxos/nxos.py
index 50f8b2fbb113c8..66ad4b88b330db 100644
--- a/lib/ansible/module_utils/network/nxos/nxos.py
+++ b/lib/ansible/module_utils/network/nxos/nxos.py
@@ -158,6 +158,28 @@ def run_commands(self, commands, check_rc=True):
         except ConnectionError as exc:
             self._module.fail_json(msg=to_text(exc))
 
+    def cli_run_commands(self, commands, check_rc=True):
+        """
+        Fallback to text command if JSON command fails for cliconf
+        when check_rc is set to False
+        """
+
+        if check_rc:
+            return self.run_commands(commands, check_rc)
+
+        out = self.run_commands(commands, check_rc)
+        capabilities = self.get_capabilities()
+        network_api = capabilities.get('network_api')
+
+        if network_api == 'cliconf' and out:
+            for index, resp in enumerate(out):
+                if 'Invalid command at' in resp and 'json' in resp:
+                    if commands[index]['output'] == 'json':
+                        commands[index]['output'] = 'text'
+                        out = self.run_commands(commands, check_rc)
+            return out
+        return out
+
     def load_config(self, config, return_error=False, opts=None, replace=None):
         """Sends configuration commands to the remote device
         """
@@ -388,6 +410,9 @@ def _send(commands, output):
 
         return responses
 
+    def cli_run_commands(self, commands, check_rc=True):
+        return self.run_commands(commands, check_rc)
+
     def load_config(self, commands, return_error=False, opts=None, replace=None):
         """Sends the ordered set of commands to the device
         """
@@ -502,6 +527,11 @@ def run_commands(module, commands, check_rc=True):
     return conn.run_commands(to_command(module, commands), check_rc)
 
 
+def cli_run_commands(module, commands, check_rc=True):
+    conn = get_connection(module)
+    return conn.cli_run_commands(to_command(module, commands), check_rc)
+
+
 def load_config(module, config, return_error=False, opts=None, replace=None):
     conn = get_connection(module)
     return conn.load_config(config, return_error, opts, replace=replace)
diff --git a/lib/ansible/modules/network/nxos/nxos_facts.py b/lib/ansible/modules/network/nxos/nxos_facts.py
index 593ca6d0b49f16..46c4a1b5e51446 100644
--- a/lib/ansible/modules/network/nxos/nxos_facts.py
+++ b/lib/ansible/modules/network/nxos/nxos_facts.py
@@ -170,7 +170,7 @@
 """
 import re
 
-from ansible.module_utils.network.nxos.nxos import run_commands, get_config
+from ansible.module_utils.network.nxos.nxos import cli_run_commands, get_config
 from ansible.module_utils.network.nxos.nxos import get_capabilities, get_interface_type
 from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
 from ansible.module_utils.basic import AnsibleModule
@@ -194,7 +194,7 @@ def run(self, command, output='text'):
             'command': command,
             'output': output
         }
-        resp = run_commands(self.module, [command], check_rc=False)
+        resp = cli_run_commands(self.module, [command], check_rc=False)
         try:
             return resp[0]
         except IndexError:
@@ -234,10 +234,8 @@ class Default(FactsBase):
     def populate(self):
         data = None
 
-        try:
-            data = self.run('show version', output='json')
-        except ConnectionError:
-            data = self.run('show version')
+        data = self.run('show version', output='json')
+
         if data:
             if isinstance(data, dict):
                 if data.get('sys_ver_str'):
@@ -300,10 +298,8 @@ def populate(self):
             self.facts['filesystems'] = self.parse_filesystems(data)
 
         data = None
-        try:
-            data = self.run('show system resources', output='json')
-        except ConnectionError:
-            data = self.run('show system resources')
+        data = self.run('show system resources', output='json')
+
         if data:
             if isinstance(data, dict):
                 self.facts['memtotal_mb'] = int(data['memory_usage_total']) / 1024
@@ -380,10 +376,8 @@ def populate(self):
         self.facts['all_ipv6_addresses'] = list()
         data = None
 
-        try:
-            data = self.run('show interface', output='json')
-        except ConnectionError:
-            data = self.run('show interface')
+        data = self.run('show interface', output='json')
+
         if data:
             if isinstance(data, dict):
                 self.facts['interfaces'] = self.populate_structured_interfaces(data)
@@ -392,10 +386,7 @@ def populate(self):
                 self.facts['interfaces'] = self.populate_interfaces(interfaces)
 
         if self.ipv6_structure_op_supported():
-            try:
-                data = self.run('show ipv6 interface', output='json')
-            except ConnectionError:
-                data = self.run('show ipv6 interface')
+            data = self.run('show ipv6 interface', output='json')
         else:
             data = None
         if data:
@@ -409,10 +400,7 @@ def populate(self):
         if data:
             self.facts['neighbors'] = self.populate_neighbors(data)
 
-        try:
-            data = self.run('show cdp neighbors detail', output='json')
-        except ConnectionError:
-            data = self.run('show cdp neighbors detail')
+        data = self.run('show cdp neighbors detail', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts['neighbors'] = self.populate_structured_neighbors_cdp(data)
@@ -718,10 +706,7 @@ class Legacy(FactsBase):
     def populate(self):
         data = None
 
-        try:
-            data = self.run('show version')
-        except ConnectionError:
-            data = self.run('show version', output='json')
+        data = self.run('show version', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts.update(self.transform_dict(data, self.VERSION_MAP))
@@ -730,50 +715,35 @@ def populate(self):
                 self.facts['_os'] = self.parse_os(data)
                 self.facts['_platform'] = self.parse_platform(data)
 
-        try:
-            data = self.run('show interface', output='json')
-        except ConnectionError:
-            data = self.run('show interface')
+        data = self.run('show interface', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts['_interfaces_list'] = self.parse_structured_interfaces(data)
             else:
                 self.facts['_interfaces_list'] = self.parse_interfaces(data)
 
-        try:
-            data = self.run('show vlan brief', output='json')
-        except ConnectionError:
-            data = self.run('show vlan brief')
+        data = self.run('show vlan brief', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts['_vlan_list'] = self.parse_structured_vlans(data)
             else:
                 self.facts['_vlan_list'] = self.parse_vlans(data)
 
-        try:
-            data = self.run('show module', output='json')
-        except ConnectionError:
-            data = self.run('show module')
+        data = self.run('show module', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts['_module'] = self.parse_structured_module(data)
             else:
                 self.facts['_module'] = self.parse_module(data)
 
-        try:
-            data = self.run('show environment fan', output='json')
-        except ConnectionError:
-            data = self.run('show environment fan')
+        data = self.run('show environment fan', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts['_fan_info'] = self.parse_structured_fan_info(data)
             else:
                 self.facts['_fan_info'] = self.parse_fan_info(data)
 
-        try:
-            data = self.run('show environment power', output='json')
-        except ConnectionError:
-            data = self.run('show environment power')
+        data = self.run('show environment power', output='json')
         if data:
             if isinstance(data, dict):
                 self.facts['_power_supply_info'] = self.parse_structured_power_supply_info(data)
diff --git a/lib/ansible/modules/network/nxos/nxos_vlan.py b/lib/ansible/modules/network/nxos/nxos_vlan.py
index 152c9c0958907b..c4961f9bd6501c 100644
--- a/lib/ansible/modules/network/nxos/nxos_vlan.py
+++ b/lib/ansible/modules/network/nxos/nxos_vlan.py
@@ -74,7 +74,6 @@
     description:
       - Set VLAN mode to classical ethernet or fabricpath.
         This is a valid option for Nexus 5000 and 7000 series.
-    default: ce
     choices: ['ce','fabricpath']
     version_added: "2.4"
   aggregate:
@@ -155,9 +154,11 @@
 
 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 get_capabilities, nxos_argument_spec
+from ansible.module_utils.network.nxos.nxos import get_config, load_config, cli_run_commands
+from ansible.module_utils.network.nxos.nxos import normalize_interface, nxos_argument_spec
 from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.connection import ConnectionError
+from ansible.module_utils.network.common.config import CustomNetworkConfig
 from ansible.module_utils.network.common.utils import remove_default_spec
 
 
@@ -191,7 +192,7 @@ def is_default_name(obj, vlan_id):
     return False
 
 
-def map_obj_to_commands(updates, module, os_platform):
+def map_obj_to_commands(updates, module):
     commands = list()
     purge = module.params['purge']
     want, have = updates
@@ -201,15 +202,11 @@ def map_obj_to_commands(updates, module, os_platform):
         name = w['name']
         interfaces = w.get('interfaces') or []
         mapped_vni = w['mapped_vni']
+        mode = w['mode']
         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)
 
@@ -258,7 +255,7 @@ def map_obj_to_commands(updates, module, os_platform):
                             else:
                                 if not is_default_name(obj_in_have, vlan_id):
                                     commands.append('no name')
-                        if key == 'vlan_state':
+                        if key == 'vlan_state' and value:
                             commands.append('state {0}'.format(value))
                         if key == 'mapped_vni':
                             if value == 'default':
@@ -271,7 +268,7 @@ def map_obj_to_commands(updates, module, os_platform):
                                 commands.append('no shutdown')
                             elif value == 'down':
                                 commands.append('shutdown')
-                        if key == 'mode':
+                        if key == 'mode' and value:
                             commands.append('mode {0}'.format(value))
                     if len(commands) > 1:
                         commands.append('exit')
@@ -367,6 +364,13 @@ def vlan_range_commands(module, have):
     return commands
 
 
+def normalize(interfaces):
+    normalized = None
+    if interfaces:
+        normalized = [normalize_interface(i) for i in interfaces]
+    return normalized
+
+
 def map_params_to_obj(module):
     obj = []
     if module.params['vlan_range']:
@@ -382,19 +386,24 @@ def map_params_to_obj(module):
             d = item.copy()
             d['vlan_id'] = str(d['vlan_id'])
             d['mapped_vni'] = str(d['mapped_vni'])
+            d['interfaces'] = normalize(d['interfaces'])
+            d['associated_interfaces'] = normalize(d['associated_interfaces'])
 
             obj.append(d)
     else:
+        interfaces = normalize(module.params['interfaces'])
+        associated_interfaces = normalize(module.params['associated_interfaces'])
+
         obj.append({
             'vlan_id': str(module.params['vlan_id']),
             'name': module.params['name'],
-            'interfaces': module.params['interfaces'],
+            'interfaces': 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'],
-            'associated_interfaces': module.params['associated_interfaces']
+            'associated_interfaces': associated_interfaces
         })
 
     return obj
@@ -408,49 +417,23 @@ def parse_admin_state(vlan):
         return 'down'
 
 
-def parse_mode(os_platform, output, vlan_id):
-    if not any(i in os_platform for i in ['5K', '7K']):
-        return None
-
-    try:
-        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
+def parse_mode(config):
+    mode = None
 
-        else:
-            return None
-    else:
-        return None
+    if config:
+        match = re.search(r'mode (\S+)', config)
+        if match:
+            mode = match.group(1)
+    return mode
 
 
-def parse_vni(module, vlan_id):
+def parse_vni(config):
     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)
+    if config:
+        match = re.search(r'vn-segment (\S+)', config)
+        if match:
+            vni = match.group(1)
     return str(vni)
 
 
@@ -482,41 +465,138 @@ def parse_interfaces(module, vlan):
     return vlan_int
 
 
-def parse_vlan_options(module, os_platform, output, vlan):
+def parse_vlan_config(netcfg, vlan_id):
+    parents = ['vlan {0}'.format(vlan_id)]
+    config = netcfg.get_section(parents)
+    return config
+
+
+def parse_vlan_options(module, netcfg, output, vlan):
     obj = {}
     vlan_id = vlan['vlanshowbr-vlanid-utf']
+    config = parse_vlan_config(netcfg, vlan_id)
+
     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['mode'] = parse_mode(config)
+    obj['mapped_vni'] = parse_vni(config)
     obj['interfaces'] = parse_interfaces(module, vlan)
     return obj
 
 
-def map_config_to_obj(module, os_platform):
+def parse_vlan_non_structured(module, netcfg, vlans):
+    objs = list()
+
+    for vlan in vlans:
+        vlan_match = re.search(r'(\d+)', vlan, re.M)
+        if vlan_match:
+            obj = {}
+            vlan_id = vlan_match.group(1)
+            obj['vlan_id'] = str(vlan_id)
+
+            name_match = re.search(r'{0}\s*(\S+)'.format(vlan_id), vlan, re.M)
+            if name_match:
+                name = name_match.group(1)
+                obj['name'] = name
+
+                state_match = re.search(r'{0}\s*{1}\s*(\S+)'.format(vlan_id, name), vlan, re.M)
+                if state_match:
+                    vlan_state_match = state_match.group(1)
+                    if vlan_state_match == 'suspended':
+                        vlan_state = 'suspend'
+                        admin_state = 'up'
+                    elif vlan_state_match == 'sus/lshut':
+                        vlan_state = 'suspend'
+                        admin_state = 'down'
+                    if vlan_state_match == 'active':
+                        vlan_state = 'active'
+                        admin_state = 'up'
+                    if vlan_state_match == 'act/lshut':
+                        vlan_state = 'active'
+                        admin_state = 'down'
+
+                    obj['vlan_state'] = vlan_state
+                    obj['admin_state'] = admin_state
+
+                    vlan = ','.join(vlan.splitlines())
+                    interfaces = list()
+                    intfs_match = re.search(r'{0}\s*{1}\s*{2}\s*(.*)'.format(vlan_id, name, vlan_state_match),
+                                            vlan, re.M)
+                    if intfs_match:
+                        intfs = intfs_match.group(1)
+                        intfs = intfs.split()
+                        for i in intfs:
+                            intf = normalize_interface(i.strip(','))
+                            interfaces.append(intf)
+
+                    if interfaces:
+                        obj['interfaces'] = interfaces
+                    else:
+                        obj['interfaces'] = None
+
+                    config = parse_vlan_config(netcfg, vlan_id)
+                    obj['mode'] = parse_mode(config)
+                    obj['mapped_vni'] = parse_vni(config)
+
+        objs.append(obj)
+
+    return objs
+
+
+def map_config_to_obj(module):
     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)
-
-        elif isinstance(vlans, dict):
-            obj = parse_vlan_options(module, os_platform, output, vlans)
-            objs.append(obj)
+    output = None
+
+    command = ['show vlan brief | json']
+    output = cli_run_commands(module, command, check_rc=False)[0]
+    if output:
+        netcfg = CustomNetworkConfig(indent=2,
+                                     contents=get_config(module, flags=['all']))
+
+        if isinstance(output, dict):
+            vlans = None
+            try:
+                vlans = output['TABLE_vlanbriefxbrief']['ROW_vlanbriefxbrief']
+            except KeyError:
+                return objs
+
+            if vlans:
+                if isinstance(vlans, list):
+                    for vlan in vlans:
+                        obj = parse_vlan_options(module, netcfg, output, vlan)
+                        objs.append(obj)
+                elif isinstance(vlans, dict):
+                    obj = parse_vlan_options(module, netcfg, output, vlans)
+                    objs.append(obj)
+        else:
+            vlans = list()
+            splitted_line = re.split(r'\n(\d+)|\n{2}', output.strip())
+
+            for line in splitted_line:
+                if not line:
+                    continue
+                if len(line) > 0:
+                    line = line.strip()
+                    if line[0].isdigit():
+                        match = re.search(r'(\d+)', line, re.M)
+                        if match:
+                            v = match.group(1)
+                            pos1 = splitted_line.index(v)
+                            pos2 = pos1 + 1
+                            vlaninfo = ''.join(splitted_line[pos1:pos2 + 1])
+                            vlans.append(vlaninfo)
+
+            if vlans:
+                objs = parse_vlan_non_structured(module, netcfg, vlans)
+            else:
+                return objs
 
     return objs
 
 
-def check_declarative_intent_params(want, module, os_platform, result):
+def check_declarative_intent_params(want, module, result):
 
     have = None
     is_delay = False
@@ -530,7 +610,7 @@ def check_declarative_intent_params(want, module, os_platform, result):
             is_delay = True
 
         if have is None:
-            have = map_config_to_obj(module, os_platform)
+            have = map_config_to_obj(module)
 
         for i in w['associated_interfaces']:
             obj_in_have = search_obj_in_list(w['vlan_id'], have)
@@ -552,7 +632,7 @@ def main():
         delay=dict(default=10, type='int'),
         state=dict(choices=['present', 'absent'], default='present', required=False),
         admin_state=dict(choices=['up', 'down'], required=False, default='up'),
-        mode=dict(choices=['ce', 'fabricpath'], required=False, default='ce'),
+        mode=dict(choices=['ce', 'fabricpath'], required=False),
     )
 
     aggregate_spec = deepcopy(element_spec)
@@ -579,22 +659,19 @@ def main():
                            mutually_exclusive=mutually_exclusive,
                            supports_check_mode=True)
 
-    info = get_capabilities(module).get('device_info', {})
-    os_platform = info.get('network_os_platform', '')
-
     warnings = list()
     result = {'changed': False}
     if warnings:
         result['warnings'] = warnings
 
-    have = map_config_to_obj(module, os_platform)
+    have = map_config_to_obj(module)
     want = map_params_to_obj(module)
 
     if module.params['vlan_range']:
         commands = vlan_range_commands(module, have)
         result['commands'] = commands
     else:
-        commands = map_obj_to_commands((want, have), module, os_platform)
+        commands = map_obj_to_commands((want, have), module)
         result['commands'] = commands
 
     if commands:
@@ -603,7 +680,7 @@ def main():
         result['changed'] = True
 
     if want:
-        check_declarative_intent_params(want, module, os_platform, result)
+        check_declarative_intent_params(want, module, result)
 
     module.exit_json(**result)
 
diff --git a/test/units/modules/network/nxos/fixtures/nxos_vlan/config.cfg b/test/units/modules/network/nxos/fixtures/nxos_vlan/config.cfg
index e69de29bb2d1d6..905d309ff09e93 100644
--- a/test/units/modules/network/nxos/fixtures/nxos_vlan/config.cfg
+++ b/test/units/modules/network/nxos/fixtures/nxos_vlan/config.cfg
@@ -0,0 +1,4 @@
+vlan 1
+  mode ce
+  state active
+  no shutdown
diff --git a/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan.txt b/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan.txt
deleted file mode 100644
index 58bcedda8f327b..00000000000000
--- a/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "TABLE_vlanbrief": {
-    "ROW_vlanbrief": {
-      "vlanshowbr-vlanid": 16777216, 
-      "vlanshowbr-vlanid-utf": 1, 
-      "vlanshowbr-vlanname": "default", 
-      "vlanshowbr-vlanstate": "active", 
-      "vlanshowbr-shutstate": "noshutdown"
-    }
-  }, 
-  "TABLE_mtuinfo": {
-    "ROW_mtuinfo": {
-      "vlanshowinfo-vlanid": 1, 
-      "vlanshowinfo-media-type": "enet", 
-      "vlanshowinfo-vlanmode": "ce-vlan"
-    }
-  }
-}
diff --git a/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_brief.txt b/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_brief.txt
new file mode 100644
index 00000000000000..ca90e5d4944076
--- /dev/null
+++ b/test/units/modules/network/nxos/fixtures/nxos_vlan/show_vlan_brief.txt
@@ -0,0 +1,11 @@
+{
+  "TABLE_vlanbriefxbrief": {
+    "ROW_vlanbriefxbrief": {
+      "vlanshowbr-vlanid": 16777216, 
+      "vlanshowbr-vlanid-utf": 1, 
+      "vlanshowbr-vlanname": "default", 
+      "vlanshowbr-vlanstate": "active", 
+      "vlanshowbr-shutstate": "noshutdown"
+    }
+  }
+}
diff --git a/test/units/modules/network/nxos/test_nxos_vlan.py b/test/units/modules/network/nxos/test_nxos_vlan.py
index ba2469eebdb9cb..c8a2aa609a2db8 100644
--- a/test/units/modules/network/nxos/test_nxos_vlan.py
+++ b/test/units/modules/network/nxos/test_nxos_vlan.py
@@ -33,8 +33,8 @@ class TestNxosVlanModule(TestNxosModule):
     def setUp(self):
         super(TestNxosVlanModule, self).setUp()
 
-        self.mock_run_commands = patch('ansible.modules.network.nxos.nxos_vlan.run_commands')
-        self.run_commands = self.mock_run_commands.start()
+        self.mock_cli_run_commands = patch('ansible.modules.network.nxos.nxos_vlan.cli_run_commands')
+        self.cli_run_commands = self.mock_cli_run_commands.start()
 
         self.mock_load_config = patch('ansible.modules.network.nxos.nxos_vlan.load_config')
         self.load_config = self.mock_load_config.start()
@@ -42,16 +42,11 @@ def setUp(self):
         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_cli_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):
@@ -68,7 +63,7 @@ def load_from_file(*args, **kwargs):
                 output.append(load_fixture('nxos_vlan', filename))
             return output
 
-        self.run_commands.side_effect = load_from_file
+        self.cli_run_commands.side_effect = load_from_file
         self.load_config.return_value = None
         self.get_config.return_value = load_fixture('nxos_vlan', 'config.cfg')
 


Code review comments:
diff_hunk: @@ -158,6 +158,28 @@ def run_commands(self, commands, check_rc=True):
         except ConnectionError as exc:
             self._module.fail_json(msg=to_text(exc))
 
+    def cli_run_commands(self, commands, check_rc=True):
+        """
+        Fallback to text command if JSON command fails for cliconf
+        when check_rc is set to False
+        """
+
+        if check_rc:
+            return self.run_commands(commands, check_rc)
+
+        out = self.run_commands(commands, check_rc)
+        capabilities = self.get_capabilities()
+        network_api = capabilities.get('network_api')
+
+        if network_api == 'cliconf' and out:
+            for index, resp in enumerate(out):
+                if 'Invalid command at' in resp and 'json' in resp:
+                    if commands[index]['output'] == 'json':
+                        commands[index]['output'] = 'text'
+                        out = self.run_commands(commands, check_rc)
+            return out
path: lib/ansible/module_utils/network/nxos/nxos.py
text: @user1:
This return statement is not required.