├── .gitignore ├── LICENSE ├── README.md ├── configs ├── boston.cfg ├── neighbors.json ├── nyc.cfg ├── richardson.cfg ├── rtp.cfg └── sjc.cfg ├── docs ├── docker.md └── nexus-module-docs.md ├── env-setup ├── example-playbooks ├── how-to │ ├── examples-command.yml │ ├── examples-copy.yml │ ├── examples-dir.yml │ ├── examples-feature.yml │ ├── examples-get_facts.yml │ ├── examples-get_interface.yml │ ├── examples-get_neighbors.yml │ ├── examples-hsrp.yml │ ├── examples-interface.yml │ ├── examples-ipv4interface.yml │ ├── examples-mtu.yml │ ├── examples-ping.yml │ ├── examples-pingg.yml │ ├── examples-portchannel.yml │ ├── examples-switchport.yml │ ├── examples-udld.yml │ ├── examples-udld_interface.yml │ ├── examples-vlan.yml │ ├── examples-vpc.yml │ ├── examples-vpc_interface.yml │ ├── examples-vrf.yml │ ├── examples-vrf_interface.yml │ ├── get-neighbors.yml │ ├── readme-example1.yml │ ├── readme-example2.yml │ ├── readme-example3.yml │ ├── readme-example4.yml │ ├── readme-example5.yml │ └── test │ │ ├── leaf.yml │ │ └── spine-vpc.yml └── use-cases │ └── datacenter-build │ ├── env-setup │ ├── host_vars │ ├── n3k1.yml │ ├── n9k1.yml │ └── n9k2.yml │ ├── hosts │ ├── roles │ ├── leaf │ │ ├── tasks │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ └── spine │ │ ├── tasks │ │ └── main.yml │ │ └── vars │ │ └── main.yml │ └── site.yml ├── files ├── .netauth └── ansible.cfg ├── group_vars ├── leaf.yml └── wan.yml ├── host_vars ├── richardson.yml ├── rtp.yml └── sjc.yml ├── hosts ├── library ├── nxos_command ├── nxos_copy ├── nxos_dir ├── nxos_feature ├── nxos_get_facts ├── nxos_get_interface ├── nxos_get_neighbors ├── nxos_hsrp ├── nxos_interface ├── nxos_ipv4_interface ├── nxos_mtu ├── nxos_ping ├── nxos_portchannel ├── nxos_save_config ├── nxos_switchport ├── nxos_udld ├── nxos_udld_interface ├── nxos_vlan ├── nxos_vpc ├── nxos_vpc_interface ├── nxos_vrf └── nxos_vrf_interface └── templates ├── neighbors.j2 └── routers.j2 /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.py[cod] 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Jason Edelman 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /configs/boston.cfg: -------------------------------------------------------------------------------- 1 | 2 | hostname boston 3 | 4 | interface mgmt0 5 | ip address 10.1.10.1 6 | 7 | ntp server 192.168.100.11 8 | snmp-server 192.168.100.12 9 | 10 | username cisco password cisco 11 | 12 | -------------------------------------------------------------------------------- /configs/neighbors.json: -------------------------------------------------------------------------------- 1 | 2 | [ 3 | { 4 | "local_interface": "mgmt0", 5 | "neighbor": "c3550", 6 | "neighbor_interface": "FastEthernet0/22", 7 | "platform": "Cisco WS-C3550-24" 8 | }, 9 | { 10 | "local_interface": "Ethernet1/1", 11 | "neighbor": "N9K2", 12 | "neighbor_interface": "Ethernet1/1", 13 | "platform": "N9K-C9396PX" 14 | }, 15 | { 16 | "local_interface": "Ethernet1/2", 17 | "neighbor": "N9K2", 18 | "neighbor_interface": "Ethernet1/2", 19 | "platform": "N9K-C9396PX" 20 | }, 21 | { 22 | "local_interface": "Ethernet2/12", 23 | "neighbor": "N9K2", 24 | "neighbor_interface": "Ethernet2/12", 25 | "platform": "N9K-C9396PX" 26 | } 27 | ] -------------------------------------------------------------------------------- /configs/nyc.cfg: -------------------------------------------------------------------------------- 1 | 2 | hostname nyc 3 | 4 | interface mgmt0 5 | ip address 10.1.20.1 6 | 7 | ntp server 192.168.100.11 8 | snmp-server 192.168.100.12 9 | 10 | username cisco password cisco 11 | 12 | -------------------------------------------------------------------------------- /configs/richardson.cfg: -------------------------------------------------------------------------------- 1 | 2 | hostname richardson 3 | 4 | interface mgmt0 5 | ip address 10.1.50.1 6 | 7 | ntp server 192.168.100.11 8 | snmp-server 192.168.100.12 9 | 10 | username cisco password cisco 11 | 12 | -------------------------------------------------------------------------------- /configs/rtp.cfg: -------------------------------------------------------------------------------- 1 | 2 | hostname rtp 3 | 4 | interface mgmt0 5 | ip address 10.1.40.1 6 | 7 | ntp server 192.168.100.11 8 | snmp-server 192.168.100.12 9 | 10 | username cisco password cisco 11 | 12 | -------------------------------------------------------------------------------- /configs/sjc.cfg: -------------------------------------------------------------------------------- 1 | 2 | hostname sjc 3 | 4 | interface mgmt0 5 | ip address 10.1.30.1 6 | 7 | ntp server 192.168.100.11 8 | snmp-server 192.168.100.12 9 | 10 | username cisco password cisco 11 | 12 | -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | ## Get Your Docker On 2 | 3 | **Step 1 - Download Docker Image** 4 | 5 | Ansible does not require a dedicated server to be used. In fact, many machines could have Ansible installed and they can be used to simultaneuously automate any given environment (not recommending that here, but it's definitely a nice option to have). 6 | 7 | > Note: This option assumes you already have Docker installed. If you don't, follow the instructions that can be found [here](https://docs.docker.com/installation/). 8 | 9 | The Docker image will come ready to go with Ansible along with the Cisco dependencies required to start automating Nexus environments. 10 | 11 | This [YouTube video](https://www.youtube.com/watch?v=ftxBCCKbVn4) goes through this process. 12 | 13 | ``` 14 | sudo docker pull jedelman8/nxos-ansible 15 | ``` 16 | 17 | Make sure you see the new image 18 | ``` 19 | sudo docker images 20 | ``` 21 | 22 | Start your first container for automating networks! 23 | ``` 24 | sudo docker run -it jedelman8/nxos-ansible 25 | ``` 26 | 27 | **Step 2 - Update hosts File** 28 | 29 | Ensure you can ping your Nexus NX-API enabled switches by name (not required, but helpful) from the new container. Here is a sample from a test container. This shows the file after adding two new entries for `n9k1` and `n9k2`. 30 | 31 | ``` 32 | root@3b5d22fa1231b:~$ cat /etc/hosts 33 | 172.17.0.23 3b5d22fa1231b 34 | 127.0.0.1 localhost 35 | 36 | #### Add your Nexus Switches here 37 | #### MUST Use the MGMT0 IP Address 38 | 39 | 10.1.10.100 n9k1 40 | 10.1.10.101 n9k2 41 | 42 | ``` 43 | 44 | `vim` is already installed in the container, so feel free to use it to modify the `hosts` file. 45 | 46 | You should now be able to `ping n9k1` to get a response back from the MANAGEMENT IP address of the Nexus switch. 47 | 48 | 49 | **Step 3 - Update Authentication File** 50 | 51 | **Edit the file** 52 | ``` 53 | root@3b5d22fa1231b:~$ sudo vim .netauth 54 | ``` 55 | 56 | Make changes using a text editor (example above is using vim) such that it follows the format below. Your file MUST continue to look the one provided (*just insert the username and password for your switches*). 57 | 58 | ``` 59 | # the .netauth file 60 | # make sure you input the proper creds for your device 61 | --- 62 | 63 | cisco: 64 | nexus: 65 | username: "cisco" 66 | password: "cisco" 67 | 68 | ``` 69 | 70 | 71 | If the username and password differs for a switch or group of switches, you must then use the username and password parameters in the Ansible playbook for each task. This can be seen in the **Automated Data Collection** playbook examples that be found below. 72 | 73 | > Note: if you are using Ansible Tower, you'll need to disable a security setting to allow the use of .netauth to work, i.e. the reading in of values of a file that exists outside of your working directory. 74 | > 75 | > Note: this section will be updated over time to include instructions for using `ansible-vault`. 76 | -------------------------------------------------------------------------------- /env-setup: -------------------------------------------------------------------------------- 1 | export ANSIBLE_HOSTS=hosts -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-command.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: command testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Get CLI raw text output for a given command 12 | - nxos_command: command='show run interface mgmt0 | inc description' host={{ inventory_hostname }} text=true type=show 13 | 14 | # Get structured JSON data for given command 15 | - nxos_command: command='show interface Ethernet1/1' host={{ inventory_hostname }} type=show 16 | 17 | # Configure secondary interface on Eth1/2 with command as string 18 | - nxos_command: command='interface Eth1/2,ip address 5.5.5.5/24 secondary' host={{ inventory_hostname }} type=config 19 | 20 | # Configure secondary interface on Eth1/2 with command as list 21 | - nxos_command: 22 | host: "{{ inventory_hostname }}" 23 | type: config 24 | command: ['interface Eth1/2','ip address 5.3.3.5/24 secondary'] -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-copy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: copy testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # copy config file from server to switch 12 | - nxos_copy: 13 | server_host=192.168.200.56 14 | server_path='/home/cisco/Public/switch_config.cfg' 15 | server_un=cisco 16 | server_pw=cisco 17 | copy_type=scp 18 | local_path='bootflash:switch_config1.cfg' 19 | vrf=management 20 | host={{ inventory_hostname }} 21 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-dir.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: copy testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Ensure directory is created on target device 12 | - nxos_dir: path='bootflash:new_config_dir' host={{ inventory_hostname }} state=present 13 | 14 | # Ensure directory is not on target device 15 | - nxos_dir: path='bootflash:new_config_dir' host={{ inventory_hostname }} state=absent 16 | 17 | # Ensure file is not on target device 18 | - nxos_dir: path='bootflash:switch_config1.cfg' host={{ inventory_hostname }} state=absent 19 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-feature.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: feature testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Ensure lacp is enabled 12 | - nxos_feature: feature=lacp state=enabled host={{ inventory_hostname }} 13 | 14 | # Ensure ospf is disabled 15 | - nxos_feature: feature=ospf state=disabled host={{ inventory_hostname }} 16 | 17 | # Ensure vpc is enabled 18 | - nxos_feature: feature=vpc state=enabled host={{ inventory_hostname }} 19 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-get_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: facts testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # retrieve facts 12 | - nxos_get_facts: host={{ inventory_hostname }} 13 | 14 | # retrieve facts with detailed info for interfaces (from 'show interface status') 15 | - nxos_get_facts: host={{ inventory_hostname }} detail=true 16 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-get_interface.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: get neighbors testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # retrieve details info and stats on an interface (from 'show interface') 12 | - nxos_get_interface: interface=Ethernet1/1 host={{ inventory_hostname }} 13 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-get_neighbors.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: get neighbors testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # retrieve cdp neighbors 12 | - nxos_get_neighbors: type=cdp host={{ inventory_hostname }} 13 | 14 | # retrieve lldp neighbors 15 | - nxos_get_neighbors: type=lldp host={{ inventory_hostname }} 16 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-hsrp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: hsrp testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # ensure hsrp is configured with following params on a SVI 12 | - nxos_hsrp: group=10 vip=10.1.1.1 priority=150 interface=vlan10 preempt=enabled host={{ inventory_hostname }} 13 | 14 | # ensure hsrp is configured with following params on a SVI 15 | - nxos_hsrp: group=10 vip=10.1.1.1 priority=150 interface=vlan10 preempt=enabled host={{ inventory_hostname }} auth_type=text auth_string=CISCO 16 | 17 | # removing hsrp config for given interface, group, and vip 18 | - nxos_hsrp: group=10 interface=vlan10 vip=10.1.1.1 host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-interface.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: interface testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Ensure an interface is a Layer 3 port and that it has the proper description 12 | - nxos_interface: interface=Ethernet1/1 description='Configured by Ansible' mode=layer3 host={{ inventory_hostname }} 13 | 14 | # Admin down an interface 15 | - nxos_interface: interface=Ethernet2/1 host={{ inventory_hostname }} admin_state=down 16 | 17 | # Remove all loopback interfaces 18 | - nxos_interface: interface=loopback state=absent host={{ inventory_hostname }} 19 | 20 | # Remove all logical interfaces 21 | - nxos_interface: interface={{ item }} state=absent host={{ inventory_hostname }} 22 | with_items: 23 | - loopback 24 | - portchannel 25 | - svi 26 | 27 | # Admin up all ethernet interfaces 28 | - nxos_interface: interface=ethernet host={{ inventory_hostname }} admin_state=up 29 | 30 | # Admin down ALL interfaces (physical and logical) 31 | - nxos_interface: interface=all host={{ inventory_hostname }} admin_state=down 32 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-ipv4interface.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: interface testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Ensure Eth1/1 has an IP address of 10.1.100.2/24 12 | - nxos_ipv4_interface: interface=Ethernet1/1 ip_addr=10.1.100.2 mask=24 host={{ inventory_hostname }} state=absent 13 | 14 | # Ensure vlan10 has an IP address of 100.1.1.3/24 15 | - nxos_ipv4_interface: interface=vlan10 ip_addr=10.1.100.3 mask=22 host={{ inventory_hostname }} 16 | 17 | # Ensure vlan10 does not have an IP address 18 | - nxos_ipv4_interface: interface=vlan10 host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-mtu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: mtu testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Ensure system mtu is 9126 12 | - nxos_mtu: sysmtu=9216 host={{ inventory_hostname }} 13 | 14 | # Config mtu on Eth1/1 (routed interface) 15 | - nxos_mtu: interface=Ethernet1/1 mtu=1600 host={{ inventory_hostname }} 16 | 17 | # Config mtu on Eth1/3 (switched interface) 18 | - nxos_mtu: interface=Ethernet1/3 mtu=9216 host={{ inventory_hostname }} 19 | 20 | # Unconfigure mtu on a given interface 21 | - nxos_mtu: interface=Ethernet1/3 mtu=9216 host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-ping.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: ping testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # test reachability to 8.8.8.8 using mgmt vrf 11 | - nxos_ping: dest=8.8.8.8 vrf=management host={{ inventory_hostname }} 12 | 13 | # Test reachability to a few different public IPs using mgmt vrf 14 | # if device has name lookups turned on, you can use names 15 | - nxos_ping: dest={{ item }} vrf=management host={{ inventory_hostname }} 16 | with_items: 17 | - 8.8.8.8 18 | - 4.4.4.4 19 | - 198.6.1.4 20 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-pingg.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: ping testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # test reachability to 8.8.8.8 using mgmt vrf 11 | - nxos_ping: dest=8.8.8.8 vrf=management host={{ inventory_hostname }} 12 | 13 | # Test reachability to a few different public IPs using mgmt vrf 14 | # if device has name lookups turned on, you can use names 15 | - nxos_ping: dest={{ item }} vrf=management host={{ inventory_hostname }} 16 | with_items: 17 | - 8.8.8.8 18 | - 4.4.4.4 19 | - 198.6.1.4 20 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-portchannel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: test portchannel 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | # Ensure port-channel 99 doesn't exist on the switch 11 | - nxos_portchannel: group=99 host={{ inventory_hostname }} state=absent 12 | 13 | # Ensure port-channel99 is created, add two members, and set to mode on 14 | - nxos_portchannel: 15 | group: 99 16 | members: ['Ethernet1/1','Ethernet1/2'] 17 | mode: 'active' 18 | host: "{{ inventory_hostname }}" 19 | state: present 20 | 21 | 22 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-switchport.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: switchport testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | # Config a switchport to be a trunk interface with native vlan 10 and carriers vlan 2-100 11 | - nxos_switchport: interface=Ethernet1/1 mode=trunk native_vlan=10 trunk_vlans=2-100 host={{ inventory_hostname }} 12 | 13 | # Config a switchport to an access port on vlan 20 14 | - nxos_switchport: interface=Ethernet1/2 mode=access access_vlan=20 host={{ inventory_hostname }} 15 | 16 | # Remove existing access port vlan configuration on a switchport (mode is required) 17 | - nxos_switchport: interface=Ethernet1/2 host={{ inventory_hostname }} mode=access state=absent 18 | 19 | # Remove existing trunk port vlan configuration on a switchport (mode is required) 20 | - nxos_switchport: interface=Ethernet1/1 host={{ inventory_hostname }} mode=trunk state=absent 21 | 22 | # Note: using absent will result in a default switchport config (access vlan 1). mode is required. -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-udld.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: udld testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # ensure udld aggressive mode is globally disabled and se global message interval is 20 11 | - nxos_udld: aggressive=disabled msg_time=20 host={{ inventory_hostname }} 12 | 13 | # Ensure agg mode is globally enabled and msg time is 15 14 | - nxos_udld: aggressive=enabled msg_time=15 host={{ inventory_hostname }} state=present 15 | 16 | # Ensure msg_time is unconfigured (if it is already 25- basically defaults back to 15 anyway) 17 | - nxos_udld: msg_time=25 host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-udld_interface.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: udld testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # ensure Ethernet1/1 is configured to be in aggressive mode 11 | - nxos_udld_interface: interface=Ethernet1/1 mode=aggressive state=present host={{ inventory_hostname }} 12 | 13 | # Remove the aggressive config only if it's currently in aggressive mode and then disabled udld (switch default) 14 | - nxos_udld_interface: interface=Ethernet1/1 mode=aggressive state=absent host={{ inventory_hostname }} 15 | 16 | # ensure Ethernet1/1 has aggressive mode enabled 17 | - nxos_udld_interface: interface=Ethernet1/1 mode=enabled host={{ inventory_hostname }} 18 | 19 | # ensure Ethernet1/1 has aggressive mode disabled 20 | - nxos_udld_interface: interface=Ethernet1/1 mode=disabled host={{ inventory_hostname }} -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-vlan.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: vlan testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | # Ensure VLAN 50 exists with the name WEB and is in the shutdown state 12 | - nxos_vlan: vlan_id=50 host={{ inventory_hostname }} admin_state=down name=WEB 13 | 14 | # Ensure VLAN is NOT on the device 15 | - nxos_vlan: vlan_id=50 host={{ inventory_hostname }} state=absent 16 | 17 | # Ensure a range of VLANs are present on the switch 18 | - nxos_vlan: vlan_id="2-10,20,50,55-60" host={{ inventory_hostname }} state=present 19 | 20 | # Ensure a group of VLANs are present with the given names 21 | - nxos_vlan: vlan_id={{ item.vlan_id }} name={{ item.name }} host={{ inventory_hostname }} state=present 22 | with_items: 23 | - vlan_id: 10 24 | name: web 25 | - vlan_id: 20 26 | name: app 27 | - { vlan_id: 30, name: db } 28 | - vlan_id: 40 29 | name: misc 30 | - vlan_id: 99 31 | name: native_vlan 32 | -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-vpc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: vpc testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # ensure vpc domain 100 is configured 11 | - nxos_vpc: domain=100 role_priority=1000 system_priority=2000 pkl_src=192.168.100.1 pkl_dest=192.168.100.2 host={{ inventory_hostname }} 12 | 13 | # ensure peer gateway is enabled for vpc domain 100 14 | - nxos_vpc: domain=100 peer_gw=true host={{ inventory_hostname }} 15 | 16 | # ensure vpc domain does not exist on switch 17 | - nxos_vpc: domain=100 host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-vpc_interface.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: vpc interface testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # config portchannel10 to be the peerlink 11 | #- nxos_vpc_interface: portchannel=10 peer_link=true host={{ inventory_hostname }} 12 | 13 | # config portchannel20 to be vpc20 14 | #- nxos_vpc_interface: portchannel=20 vpc=20 host={{ inventory_hostname }} 15 | 16 | # remove whatever VPC config is on portchannel if any exists (vpc xx or vpc peer-link) 17 | - nxos_vpc_interface: portchannel=80 host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-vrf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: vrf testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # ensure yankees VRF exists on switch 11 | # description is not idempotent 12 | - nxos_vrf: vrf=yankees host={{ inventory_hostname }} 13 | 14 | # ensure yankees VRF does not exist on switch 15 | - nxos_vrf: vrf=yankees host={{ inventory_hostname }} state=absent -------------------------------------------------------------------------------- /example-playbooks/how-to/examples-vrf_interface.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: vrf interface testing 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | tasks: 9 | 10 | # ensure vrf yankees exists on Eth1/1 11 | - nxos_vrf_interface: vrf=yankees interface=Ethernet1/1 host={{ inventory_hostname }} state=present 12 | 13 | # ensure yankees VRF does not exist on Eth1/1 14 | - nxos_vrf_interface: vrf=yankees interface=Ethernet1/1 host={{ inventory_hostname }} state=absent 15 | 16 | ##### vrf isn't read from interface properly until and IP Address is set. 17 | ###### removing a vrf still removes l3 configs -------------------------------------------------------------------------------- /example-playbooks/how-to/get-neighbors.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: get neighbor data 4 | hosts: n9k1 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | - name: get neighbors 12 | nxos_get_neighbors: type=cdp host={{ inventory_hostname }} 13 | register: my_neighbors 14 | 15 | - name: print data to file 16 | template: src=templates/neighbors.j2 dest=configs/neighbors.json 17 | -------------------------------------------------------------------------------- /example-playbooks/how-to/readme-example1.yml: -------------------------------------------------------------------------------- 1 | # examples/readme-example1.yml 2 | --- 3 | 4 | - name: example 1 - baseline 5 | hosts: all 6 | connection: local 7 | gather_facts: no 8 | 9 | 10 | tasks: 11 | - name: ensure no logical interfaces exist on the switch 12 | nxos_interface: interface={{ item }} state=absent host={{ inventory_hostname }} 13 | with_items: 14 | - loopback 15 | - portchannel 16 | - svi 17 | 18 | - name: ensure all Ethernet ports are admin down 19 | nxos_interface: interface=Ethernet admin_state=down host={{ inventory_hostname }} -------------------------------------------------------------------------------- /example-playbooks/how-to/readme-example2.yml: -------------------------------------------------------------------------------- 1 | # examples/readme-example2.yml 2 | --- 3 | 4 | - name: example 2 - VLANs 5 | hosts: all 6 | connection: local 7 | gather_facts: no 8 | 9 | 10 | tasks: 11 | - name: ensure VLANs 2-20 and 99 exist on all switches 12 | nxos_vlan: vlan_id="2-20,99" state=present host={{ inventory_hostname }} 13 | 14 | - name: config VLANs names for a few VLANs 15 | nxos_vlan: vlan_id={{ item.vid }} name={{ item.name }} host={{ inventory_hostname }} state=present 16 | with_items: 17 | - { vid: 2, name: web } 18 | - { vid: 3, name: app } 19 | - { vid: 4, name: db } 20 | - { vid: 20, name: server } 21 | - { vid: 99, name: native } -------------------------------------------------------------------------------- /example-playbooks/how-to/readme-example3.yml: -------------------------------------------------------------------------------- 1 | # examples/readme-example3.yml 2 | --- 3 | 4 | - name: example 3 - play 1 - spine interfaces 5 | hosts: spine 6 | connection: local 7 | gather_facts: no 8 | 9 | 10 | tasks: 11 | 12 | - name: ensure following ports are defaulted 13 | nxos_interface: interface={{ item }} state=default host={{ inventory_hostname }} 14 | with_items: 15 | - Ethernet1/1 16 | - Ethernet1/2 17 | - Ethernet1/3 18 | - Ethernet1/4 19 | - Ethernet2/9 20 | - Ethernet2/10 21 | - Ethernet2/11 22 | - Ethernet2/12 23 | 24 | - name: vlans for a portchannel 25 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=2-20 host={{ inventory_hostname }} 26 | with_items: 27 | - Ethernet1/1 28 | - Ethernet1/2 29 | 30 | - name: example 3 - play 2 - leaf interfaces 31 | hosts: leaf 32 | connection: local 33 | gather_facts: no 34 | 35 | 36 | tasks: 37 | 38 | - name: ensure ports in scope in this pb are default interfaces 39 | nxos_interface: interface={{ item }} state=default host={{ inventory_hostname }} 40 | with_items: leaf_ports 41 | 42 | - name: config for a few interfaces on leafs 43 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=2-20 host={{ inventory_hostname }} 44 | with_items: leaf_ports 45 | # note: leaf_ports is a variable defined in /home/cisco/nxos-ansible/group_vars/leaf.yml -------------------------------------------------------------------------------- /example-playbooks/how-to/readme-example4.yml: -------------------------------------------------------------------------------- 1 | # examples/readme-example4.yml 2 | --- 3 | 4 | - name: example 4 - play 1 - spine portchannels 5 | hosts: spine 6 | connection: local 7 | gather_facts: no 8 | 9 | 10 | tasks: 11 | 12 | - name: portchannel 10 facing a leaf 13 | nxos_portchannel: 14 | group: 10 15 | members: ['Ethernet1/1','Ethernet1/2'] 16 | mode: 'active' 17 | host: "{{ inventory_hostname }}" 18 | state: present 19 | 20 | 21 | - name: example 4 - play 2 - leaf portchannels 22 | hosts: leaf 23 | connection: local 24 | gather_facts: no 25 | 26 | tasks: 27 | 28 | - name: portchannel 100 facing spine 29 | nxos_portchannel: 30 | group: 100 31 | members: ['Ethernet1/1','Ethernet1/2'] 32 | mode: 'active' 33 | host: "{{ inventory_hostname }}" 34 | state: present -------------------------------------------------------------------------------- /example-playbooks/how-to/readme-example5.yml: -------------------------------------------------------------------------------- 1 | # examples/readme-example5.yml 2 | --- 3 | 4 | - name: example 5 - get neighbors 5 | hosts: spine 6 | connection: local 7 | gather_facts: no 8 | 9 | 10 | tasks: 11 | 12 | # also showing username/password as params 13 | # you can remove or insert the correct values 14 | 15 | - name: get neighbors 16 | nxos_get_neighbors: type=cdp host={{ inventory_hostname }} username=admin password=cisco123 -------------------------------------------------------------------------------- /example-playbooks/how-to/test/leaf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: leaf configuration 4 | hosts: leaf 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | ############################################################ 12 | # VLAN CONFIGURATION 13 | ############################################################ 14 | - name: ensure VLANs exist on switches 15 | nxos_vlan: vlan_id="2-19,99" state=present host={{ inventory_hostname }} 16 | 17 | - name: ensure names exist for important vlans 18 | nxos_vlan: vlan_id={{ item.vlan_id }} name={{ item.name }} host={{ inventory_hostname }} state=present 19 | with_items: 20 | - { vlan_id: 10, name: test_segment } 21 | - { vlan_id: 99, name: native } 22 | 23 | 24 | ############################################################# 25 | # Layer 2 switchport configs 26 | ############################################################# 27 | - name: L2 config for uplinks 28 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=2-19 host={{ inventory_hostname }} 29 | with_items: 30 | - Ethernet1/49 31 | - Ethernet1/50 32 | 33 | ############################################################# 34 | # Portchannels 35 | ############################################################# 36 | 37 | 38 | - nxos_portchannel: 39 | group: 100 40 | members: ['Ethernet1/49','Ethernet1/50'] 41 | mode: active 42 | host: "{{ inventory_hostname }}" 43 | state: present 44 | 45 | 46 | ############################################################# 47 | # Layer 3 Configuration 48 | ############################################################# 49 | 50 | - name: create logical vlan interfaces 51 | nxos_interface: interface={{ item }} host={{ inventory_hostname }} 52 | with_items: 53 | - vlan10 54 | 55 | - name: assign IP addresses 56 | nxos_ipv4_interface: interface=vlan10 ip_addr=10.1.10.10 mask=24 host={{ inventory_hostname }} 57 | -------------------------------------------------------------------------------- /example-playbooks/how-to/test/spine-vpc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: spine vpc configuration 4 | hosts: spine 5 | connection: local 6 | gather_facts: no 7 | 8 | 9 | tasks: 10 | 11 | ############################################################ 12 | # VLAN CONFIGURATION 13 | ############################################################ 14 | - name: ensure VLANs exist on switches 15 | nxos_vlan: vlan_id="2-20,99" state=present host={{ inventory_hostname }} 16 | 17 | - name: ensure names exist for important vlans 18 | nxos_vlan: vlan_id={{ item.vlan_id }} name={{ item.name }} host={{ inventory_hostname }} state=present 19 | with_items: 20 | - { vlan_id: 10, name: test_segment } 21 | - { vlan_id: 20, name: peer_keepalive } 22 | - { vlan_id: 99, name: native } 23 | 24 | 25 | ############################################################# 26 | # Layer 2 switchport configs 27 | ############################################################# 28 | - name: L2 config for all ports except peer keepalive 29 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=2-20 host={{ inventory_hostname }} 30 | with_items: 31 | - Ethernet1/1 32 | - Ethernet1/2 33 | - Ethernet2/1 34 | 35 | - name: L2 config for peer keepalive link 36 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=20 host={{ inventory_hostname }} 37 | with_items: 38 | - Ethernet2/12 39 | 40 | 41 | 42 | ############################################################# 43 | # Portchannels 44 | ############################################################# 45 | 46 | 47 | - nxos_portchannel: 48 | group: 10 49 | members: ['Ethernet1/1','Ethernet1/2'] 50 | mode: active 51 | host: "{{ inventory_hostname }}" 52 | state: present 53 | 54 | - nxos_portchannel: 55 | group: 11 56 | members: ['Ethernet2/1'] 57 | mode: active 58 | host: "{{ inventory_hostname }}" 59 | state: present 60 | 61 | - nxos_portchannel: 62 | group: 12 63 | members: ['Ethernet2/12'] 64 | mode: active 65 | host: "{{ inventory_hostname }}" 66 | state: present 67 | 68 | 69 | ############################################################# 70 | # Layer 3 Configuration 71 | ############################################################# 72 | 73 | - name: create logical vlan interfaces 74 | nxos_interface: interface={{ item }} host={{ inventory_hostname }} 75 | with_items: 76 | - vlan10 77 | - vlan20 78 | 79 | - name: ensure VRF for peer keepalive is created 80 | nxos_vrf: vrf=keepalive host={{ inventory_hostname }} 81 | 82 | - name: assgn VRF to peer keepalive link interfaces 83 | nxos_vrf_interface: interface=vlan20 vrf=keepalive host={{ inventory_hostname }} 84 | 85 | - name: assign IP addresses 86 | nxos_ipv4_interface: interface={{ item.interface }} ip_addr={{ item.ip }} mask={{ item.mask }} host={{ inventory_hostname }} 87 | with_items: layer3ip 88 | 89 | - name: config HSRP 90 | nxos_hsrp: group={{ item.group }} interface={{ item.interface }} vip={{ item.vip }} priority={{ hsrp_priority }} host={{ inventory_hostname }} 91 | with_items: hsrp 92 | 93 | ############################################################# 94 | # Global VPC Configuration 95 | ############################################################# 96 | 97 | - name: global vpc configuration params 98 | nxos_vpc: 99 | domain={{ item.value.domain }} 100 | system_priority={{ item.value.systempri }} 101 | role_priority={{ item.value.rolepri }} 102 | pkl_src={{ item.value.pkl.src }} 103 | pkl_dest={{ item.value.pkl.dest }} 104 | pkl_vrf={{ item.value.pkl.vrf }} 105 | host={{ inventory_hostname }} 106 | with_dict: vpc 107 | 108 | ############################################################# 109 | # Portchannel VPC Configuration 110 | ############################################################# 111 | 112 | - name: portchannel vpc peer link configuration 113 | nxos_vpc_interface: portchannel=10 peer_link=true host={{ inventory_hostname }} 114 | 115 | - name: portchannel vpc configuration 116 | nxos_vpc_interface: portchannel=11 vpc=11 host={{ inventory_hostname }} 117 | -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/env-setup: -------------------------------------------------------------------------------- 1 | export ANSIBLE_HOSTS=hosts -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/host_vars/n3k1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | layer3ip: 4 | - { interface: vlan10, ip: 10.1.10.10, mask: 24 } 5 | 6 | -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/host_vars/n9k1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | layer3ip: 4 | - { interface: vlan10, ip: 10.1.10.2, mask: 24 } 5 | - { interface: vlan20, ip: 10.1.20.2, mask: 24 } 6 | 7 | vpc: 8 | dummy: 9 | domain: 100 10 | systempri: 2000 11 | rolepri: 1000 12 | pkl: 13 | src: 10.1.20.2 14 | dest: 10.1.20.3 15 | vrf: keepalive 16 | 17 | hsrp_priority: 120 -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/host_vars/n9k2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | layer3ip: 4 | - { interface: vlan10, ip: 10.1.10.3, mask: 24 } 5 | - { interface: vlan20, ip: 10.1.20.3, mask: 24 } 6 | 7 | vpc: 8 | dummy: 9 | domain: 100 10 | systempri: 2000 11 | rolepri: 2000 12 | pkl: 13 | src: 10.1.20.3 14 | dest: 10.1.20.2 15 | vrf: keepalive 16 | 17 | hsrp_priority: 100 -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/hosts: -------------------------------------------------------------------------------- 1 | [all:vars] 2 | ansible_connection=local 3 | 4 | [spine] 5 | n9k1 6 | n9k2 7 | 8 | [leaf] 9 | n3k1 -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/roles/leaf/tasks/main.yml: -------------------------------------------------------------------------------- 1 | 2 | ############################################################ 3 | # VLAN CONFIGURATION 4 | ############################################################ 5 | - name: ensure VLANs exist on switches 6 | nxos_vlan: vlan_id={{ vlans }} state=present host={{ inventory_hostname }} 7 | 8 | - name: ensure names exist for important vlans 9 | nxos_vlan: vlan_id={{ item.vlan_id }} name={{ item.name }} host={{ inventory_hostname }} state=present 10 | with_items: named_leaf_vlans 11 | 12 | 13 | ############################################################# 14 | # Layer 2 switchport configs 15 | ############################################################# 16 | - name: L2 config for uplinks 17 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=2-19 host={{ inventory_hostname }} 18 | with_items: uplink_interfaces 19 | 20 | ############################################################# 21 | # Portchannels 22 | ############################################################# 23 | - name: portchannel for uplinks 24 | nxos_portchannel: 25 | group: 100 26 | members: "{{ uplink_interfaces }}" 27 | mode: active 28 | host: "{{ inventory_hostname }}" 29 | state: present 30 | 31 | ############################################################# 32 | # Layer 3 Configuration 33 | ############################################################# 34 | 35 | - name: create logical vlan interfaces 36 | nxos_interface: interface={{ item }} host={{ inventory_hostname }} 37 | with_items: logical_interfaces_to_create 38 | 39 | - name: assign IP addresses 40 | nxos_ipv4_interface: interface={{ item.interface }} ip_addr={{ item.ip }} mask={{ item.mask }} host={{ inventory_hostname }} 41 | with_items: layer3ip -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/roles/leaf/vars/main.yml: -------------------------------------------------------------------------------- 1 | 2 | vlans: "2-19,99" 3 | 4 | named_leaf_vlans: 5 | - { vlan_id: 10, name: test_segment } 6 | - { vlan_id: 99, name: native } 7 | 8 | uplink_interfaces: 9 | - Ethernet1/49 10 | - Ethernet1/50 11 | 12 | logical_interfaces_to_create: 13 | - vlan10 14 | 15 | 16 | -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/roles/spine/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ############################################################ 4 | # VLAN CONFIGURATION 5 | ############################################################ 6 | - name: ensure VLANs exist on switches 7 | nxos_vlan: vlan_id={{ vlans }} state=present host={{ inventory_hostname }} 8 | 9 | - name: ensure names exist for important vlans 10 | nxos_vlan: vlan_id={{ item.vlan_id }} name={{ item.name }} host={{ inventory_hostname }} state=present 11 | with_items: named_spine_vlans 12 | 13 | 14 | ############################################################# 15 | # Layer 2 Switchport Configs 16 | ############################################################# 17 | - name: L2 config for all ports except peer keepalive 18 | nxos_switchport: interface={{ item }} mode=trunk native_vlan={{ native }} trunk_vlans={{ trunk_vlans }} host={{ inventory_hostname }} 19 | with_items: default_trunk_interfaces 20 | 21 | - name: L2 config for peer keepalive link 22 | nxos_switchport: interface={{ item }} mode=trunk native_vlan=99 trunk_vlans=20 host={{ inventory_hostname }} 23 | with_items: pkl_interfaces 24 | 25 | 26 | ############################################################# 27 | # Portchannels 28 | ############################################################# 29 | 30 | - name: configure portchannels 31 | nxos_portchannel: 32 | group: "{{ item.key }}" 33 | members: "{{ item.value.members }}" 34 | mode: "{{ item.value.mode }}" 35 | host: "{{ inventory_hostname }}" 36 | with_dict: portchannels 37 | 38 | 39 | ############################################################# 40 | # Layer 3 Configuration 41 | ############################################################# 42 | 43 | - name: create logical interfaces 44 | nxos_interface: interface={{ item }} host={{ inventory_hostname }} 45 | with_items: logical_interfaces_to_create 46 | 47 | - name: ensure VRF are created 48 | nxos_vrf: vrf={{ item }} host={{ inventory_hostname }} 49 | with_items: vrfs 50 | 51 | - name: assgn VRF to peer keepalive link interfaces 52 | nxos_vrf_interface: interface={{ pkl_link.interface }} vrf={{ pkl_link.vrf }} host={{ inventory_hostname }} 53 | 54 | - name: assign IP addresses 55 | nxos_ipv4_interface: interface={{ item.interface }} ip_addr={{ item.ip }} mask={{ item.mask }} host={{ inventory_hostname }} 56 | with_items: layer3ip 57 | 58 | - name: config HSRP 59 | nxos_hsrp: group={{ item.group }} interface={{ item.interface }} vip={{ item.vip }} priority={{ hsrp_priority }} host={{ inventory_hostname }} 60 | with_items: hsrp 61 | 62 | ############################################################# 63 | # Global VPC Configuration 64 | ############################################################# 65 | 66 | - name: global vpc configuration params 67 | nxos_vpc: 68 | domain={{ item.value.domain }} 69 | system_priority={{ item.value.systempri }} 70 | role_priority={{ item.value.rolepri }} 71 | pkl_src={{ item.value.pkl.src }} 72 | pkl_dest={{ item.value.pkl.dest }} 73 | pkl_vrf={{ item.value.pkl.vrf }} 74 | host={{ inventory_hostname }} 75 | with_dict: vpc 76 | 77 | 78 | ############################################################# 79 | # Portchannel VPC Configuration 80 | ############################################################# 81 | 82 | - name: portchannel vpc peer link configuration 83 | nxos_vpc_interface: portchannel=10 peer_link=true host={{ inventory_hostname }} 84 | 85 | - name: portchannel vpc configuration 86 | nxos_vpc_interface: portchannel=11 vpc=11 host={{ inventory_hostname }} 87 | -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/roles/spine/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | vlans: "2-20,99" 4 | 5 | trunk_vlans: "2-20" 6 | native: 99 7 | 8 | named_spine_vlans: 9 | - { vlan_id: 10, name: test_segment } 10 | - { vlan_id: 20, name: peer_keepalive } 11 | - { vlan_id: 99, name: native } 12 | 13 | 14 | default_trunk_interfaces: 15 | - Ethernet1/1 16 | - Ethernet1/2 17 | - Ethernet2/1 18 | 19 | pkl_interfaces: 20 | - Ethernet2/12 21 | 22 | logical_interfaces_to_create: 23 | - vlan10 24 | - vlan20 25 | 26 | vrfs: 27 | - keepalive 28 | 29 | pkl_link: 30 | vrf: keepalive 31 | interface: vlan20 32 | 33 | hsrp: 34 | - { interface: vlan10, vip: 10.1.10.1, group: 10 } 35 | - { interface: vlan20, vip: 10.1.20.1, group: 20 } 36 | 37 | portchannels: 38 | 10: 39 | members: 40 | - Ethernet1/1 41 | - Ethernet1/2 42 | mode: active 43 | 11: 44 | members: 45 | - Ethernet2/1 46 | mode: active 47 | 12: 48 | members: 49 | - Ethernet2/12 50 | mode: active 51 | 52 | -------------------------------------------------------------------------------- /example-playbooks/use-cases/datacenter-build/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This Playbook stands up dual-core and a single leaf 3 | # It assigns an IP to an SVI on the leaf to be used for 4 | # testing connectivity without any hosts 5 | # Add more host_vars files to scale out 6 | 7 | 8 | - hosts: spine 9 | roles: 10 | - role: spine 11 | 12 | - hosts: leaf 13 | roles: 14 | - role: leaf 15 | 16 | - hosts: all 17 | tasks: 18 | - name: save config 19 | nxos_save_config: host={{ inventory_hostname }} 20 | 21 | -------------------------------------------------------------------------------- /files/.netauth: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | cisco: 4 | nexus: 5 | username: "cisco" 6 | password: "cisco" 7 | 8 | -------------------------------------------------------------------------------- /files/ansible.cfg: -------------------------------------------------------------------------------- 1 | # config file for ansible -- http://ansible.com/ 2 | # ============================================== 3 | 4 | # nearly all parameters can be overridden in ansible-playbook 5 | # or with command line flags. ansible will read ANSIBLE_CONFIG, 6 | # ansible.cfg in the current working directory, .ansible.cfg in 7 | # the home directory or /etc/ansible/ansible.cfg, whichever it 8 | # finds first 9 | 10 | [defaults] 11 | 12 | # some basic default values... 13 | 14 | hostfile = /etc/ansible/hosts 15 | library = /usr/share/ansible 16 | remote_tmp = $HOME/.ansible/tmp 17 | pattern = * 18 | forks = 5 19 | poll_interval = 15 20 | sudo_user = root 21 | #ask_sudo_pass = True 22 | #ask_pass = True 23 | transport = smart 24 | remote_port = 22 25 | module_lang = C 26 | 27 | # plays will gather facts by default, which contain information about 28 | # the remote system. 29 | # 30 | # smart - gather by default, but don't regather if already gathered 31 | # implicit - gather by default, turn off with gather_facts: False 32 | # explicit - do not gather by default, must say gather_facts: True 33 | gathering = explicit 34 | 35 | # additional paths to search for roles in, colon separated 36 | #roles_path = /etc/ansible/roles 37 | 38 | # uncomment this to disable SSH key host checking 39 | #host_key_checking = False 40 | 41 | # change this for alternative sudo implementations 42 | sudo_exe = sudo 43 | 44 | # what flags to pass to sudo 45 | #sudo_flags = -H 46 | 47 | # SSH timeout 48 | timeout = 10 49 | 50 | # default user to use for playbooks if user is not specified 51 | # (/usr/bin/ansible will use current user as default) 52 | #remote_user = root 53 | 54 | # logging is off by default unless this path is defined 55 | # if so defined, consider logrotate 56 | #log_path = /var/log/ansible.log 57 | 58 | # default module name for /usr/bin/ansible 59 | #module_name = command 60 | 61 | # use this shell for commands executed under sudo 62 | # you may need to change this to bin/bash in rare instances 63 | # if sudo is constrained 64 | #executable = /bin/sh 65 | 66 | # if inventory variables overlap, does the higher precedence one win 67 | # or are hash values merged together? The default is 'replace' but 68 | # this can also be set to 'merge'. 69 | #hash_behaviour = replace 70 | 71 | # list any Jinja2 extensions to enable here: 72 | #jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n 73 | 74 | # if set, always use this private key file for authentication, same as 75 | # if passing --private-key to ansible or ansible-playbook 76 | #private_key_file = /path/to/file 77 | 78 | # format of string {{ ansible_managed }} available within Jinja2 79 | # templates indicates to users editing templates files will be replaced. 80 | # replacing {file}, {host} and {uid} and strftime codes with proper values. 81 | ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host} 82 | 83 | # by default, ansible-playbook will display "Skipping [host]" if it determines a task 84 | # should not be run on a host. Set this to "False" if you don't want to see these "Skipping" 85 | # messages. NOTE: the task header will still be shown regardless of whether or not the 86 | # task is skipped. 87 | #display_skipped_hosts = True 88 | 89 | # by default (as of 1.3), Ansible will raise errors when attempting to dereference 90 | # Jinja2 variables that are not set in templates or action lines. Uncomment this line 91 | # to revert the behavior to pre-1.3. 92 | #error_on_undefined_vars = False 93 | 94 | # by default (as of 1.6), Ansible may display warnings based on the configuration of the 95 | # system running ansible itself. This may include warnings about 3rd party packages or 96 | # other conditions that should be resolved if possible. 97 | # to disable these warnings, set the following value to False: 98 | #system_warnings = True 99 | 100 | # by default (as of 1.4), Ansible may display deprecation warnings for language 101 | # features that should no longer be used and will be removed in future versions. 102 | # to disable these warnings, set the following value to False: 103 | #deprecation_warnings = True 104 | 105 | # set plugin path directories here, separate with colons 106 | action_plugins = /usr/share/ansible_plugins/action_plugins 107 | callback_plugins = /usr/share/ansible_plugins/callback_plugins 108 | connection_plugins = /usr/share/ansible_plugins/connection_plugins 109 | lookup_plugins = /usr/share/ansible_plugins/lookup_plugins 110 | vars_plugins = /usr/share/ansible_plugins/vars_plugins 111 | filter_plugins = /usr/share/ansible_plugins/filter_plugins 112 | 113 | # don't like cows? that's unfortunate. 114 | # set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1 115 | #nocows = 1 116 | 117 | # don't like colors either? 118 | # set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1 119 | #nocolor = 1 120 | 121 | # the CA certificate path used for validating SSL certs. This path 122 | # should exist on the controlling node, not the target nodes 123 | # common locations: 124 | # RHEL/CentOS: /etc/pki/tls/certs/ca-bundle.crt 125 | # Fedora : /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem 126 | # Ubuntu : /usr/share/ca-certificates/cacert.org/cacert.org.crt 127 | #ca_file_path = 128 | 129 | # the http user-agent string to use when fetching urls. Some web server 130 | # operators block the default urllib user agent as it is frequently used 131 | # by malicious attacks/scripts, so we set it to something unique to 132 | # avoid issues. 133 | #http_user_agent = ansible-agent 134 | 135 | [paramiko_connection] 136 | 137 | # uncomment this line to cause the paramiko connection plugin to not record new host 138 | # keys encountered. Increases performance on new host additions. Setting works independently of the 139 | # host key checking setting above. 140 | #record_host_keys=False 141 | 142 | # by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this 143 | # line to disable this behaviour. 144 | #pty=False 145 | 146 | [ssh_connection] 147 | 148 | # ssh arguments to use 149 | # Leaving off ControlPersist will result in poor performance, so use 150 | # paramiko on older platforms rather than removing it 151 | #ssh_args = -o ControlMaster=auto -o ControlPersist=60s 152 | 153 | # The path to use for the ControlPath sockets. This defaults to 154 | # "%(directory)s/ansible-ssh-%%h-%%p-%%r", however on some systems with 155 | # very long hostnames or very long path names (caused by long user names or 156 | # deeply nested home directories) this can exceed the character limit on 157 | # file socket names (108 characters for most platforms). In that case, you 158 | # may wish to shorten the string below. 159 | # 160 | # Example: 161 | # control_path = %(directory)s/%%h-%%r 162 | #control_path = %(directory)s/ansible-ssh-%%h-%%p-%%r 163 | 164 | # Enabling pipelining reduces the number of SSH operations required to 165 | # execute a module on the remote server. This can result in a significant 166 | # performance improvement when enabled, however when using "sudo:" you must 167 | # first disable 'requiretty' in /etc/sudoers 168 | # 169 | # By default, this option is disabled to preserve compatibility with 170 | # sudoers configurations that have requiretty (the default on many distros). 171 | # 172 | #pipelining = False 173 | 174 | # if True, make ansible use scp if the connection type is ssh 175 | # (default is sftp) 176 | #scp_if_ssh = True 177 | 178 | [accelerate] 179 | accelerate_port = 5099 180 | accelerate_timeout = 30 181 | accelerate_connect_timeout = 5.0 182 | 183 | # The daemon timeout is measured in minutes. This time is measured 184 | # from the last activity to the accelerate daemon. 185 | accelerate_daemon_timeout = 30 186 | 187 | # If set to yes, accelerate_multi_key will allow multiple 188 | # private keys to be uploaded to it, though each user must 189 | # have access to the system via SSH to add a new key. The default 190 | # is "no". 191 | #accelerate_multi_key = yes 192 | 193 | -------------------------------------------------------------------------------- /group_vars/leaf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | leaf_ports: 4 | - Ethernet1/1 5 | - Ethernet1/2 -------------------------------------------------------------------------------- /group_vars/wan.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ntp_server: 192.168.100.11 3 | snmp_server: 192.168.100.12 4 | -------------------------------------------------------------------------------- /host_vars/richardson.yml: -------------------------------------------------------------------------------- 1 | --- 2 | mgmt_ip: 10.1.50.1 3 | -------------------------------------------------------------------------------- /host_vars/rtp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | mgmt_ip: 10.1.40.1 3 | -------------------------------------------------------------------------------- /host_vars/sjc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | mgmt_ip: 10.1.30.1 3 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [all:vars] 2 | ansible_connection = local 3 | 4 | [spine] 5 | n9k1 6 | n9k2 7 | 8 | [leaf] 9 | n9k3 10 | n9k4 11 | n3k1 12 | 13 | [wan] 14 | boston mgmt_ip=10.1.10.1 15 | nyc mgmt_ip=10.1.20.1 16 | sjc 17 | rtp 18 | richardson 19 | -------------------------------------------------------------------------------- /library/nxos_command: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_command 21 | short_description: Send raw commands to Cisco NX-API enabled devices 22 | description: 23 | - Raw show and config commands can be sent to NX-API enabled devices. 24 | For show commands there is the ability to return structured 25 | or raw text data. 26 | The command param when type=config can be a list or string with commands 27 | separated by a comma. 28 | author: Jason Edelman (@jedelman8) 29 | notes: 30 | - Only a single show command can be sent per task while multiple 31 | config commands can be sent. 32 | - Single show command or list of config commands or series of config 33 | commands separated by a comma supported 34 | - While username and password are not required params, they are 35 | if you are not using the .netauth file. .netauth file is recommended 36 | as it will clean up the each task in the playbook by not requiring 37 | the username and password params for every tasks. 38 | - Using the username and password params will override the .netauth file 39 | requirements: 40 | - NX-API 1.0 41 | - NX-OS 6.1(2)I3(1) 42 | - pycsco 43 | options: 44 | command: 45 | description: 46 | - Show command as a string or a string of config commands 47 | separated by a comma or a list of config 48 | commands (complex args in Ansible) 49 | required: true 50 | default: null 51 | choices: [] 52 | aliases: [] 53 | type: 54 | description: 55 | - Represents the type of command being sent to the device 56 | required: true 57 | default: null 58 | choices: ['show','config'] 59 | aliases: [] 60 | text: 61 | description: 62 | - Dictates how data will be returned for show commands. 63 | Set to true if NX-API doesn't support structured output 64 | for a given command 65 | required: false 66 | default: null 67 | choices: [true,false] 68 | aliases: [] 69 | host: 70 | description: 71 | - IP Address or hostname (resolvable by Ansible control host) 72 | of the target NX-API enabled switch 73 | required: true 74 | default: null 75 | choices: [] 76 | aliases: [] 77 | username: 78 | description: 79 | - Username used to login to the switch 80 | required: false 81 | default: null 82 | choices: [] 83 | aliases: [] 84 | password: 85 | description: 86 | - Password used to login to the switch 87 | required: false 88 | default: null 89 | choices: [] 90 | aliases: [] 91 | protocol: 92 | description: 93 | - Dictates connection protocol to use for NX-API 94 | required: false 95 | default: http 96 | choices: ['http', 'https'] 97 | aliases: [] 98 | ''' 99 | 100 | EXAMPLES = ''' 101 | # Get CLI raw text output for a given command 102 | - nxos_command: command='show run interface mgmt0 | inc description' host={{ inventory_hostname }} text=true type=show 103 | 104 | # Get structured JSON data for given command 105 | - nxos_command: command='show interface Ethernet1/1' host={{ inventory_hostname }} type=show 106 | 107 | # Configure secondary interface on Eth1/2 with command as string 108 | - nxos_command: command='interface Eth1/2,ip address 5.5.5.5/24 secondary' host={{ inventory_hostname }} type=config 109 | 110 | # Configure secondary interface on Eth1/2 with command as list 111 | - nxos_command: 112 | host: "{{ inventory_hostname }}" 113 | type: config 114 | command: ['interface Eth1/2','ip address 5.3.3.5/24 secondary'] 115 | ''' 116 | 117 | try: 118 | import xmltodict 119 | import socket 120 | from pycsco.nxos.device import Device 121 | from pycsco.nxos.device import Auth 122 | from pycsco.nxos.utils import nxapi_lib 123 | except ImportError as e: 124 | print '*' * 30 125 | print e 126 | print '*' * 30 127 | 128 | 129 | def main(): 130 | 131 | module = AnsibleModule( 132 | argument_spec=dict( 133 | command=dict(required=True), 134 | text=dict(choices=BOOLEANS, type='bool'), 135 | type=dict(choices=['show', 'config'], required=True), 136 | host=dict(required=True), 137 | username=dict(type='str'), 138 | password=dict(type='str') 139 | ), 140 | supports_check_mode=False 141 | ) 142 | 143 | auth = Auth(vendor='cisco', model='nexus') 144 | username = module.params['username'] or auth.username 145 | password = module.params['password'] or auth.password 146 | 147 | host = socket.gethostbyname(module.params['host']) 148 | 149 | command = module.params['command'] 150 | text = module.params['text'] or False 151 | cmd_type = module.params['type'].lower() 152 | 153 | device = Device(ip=host, username=username, password=password, 154 | protocol=protocol) 155 | 156 | if cmd_type == 'show': 157 | if isinstance(command, list): 158 | module.fail_json(msg='Only single show commands are supported. ' 159 | + 'Lists are only supported for config changes ' 160 | + 'at this time.') 161 | if cmd_type == 'config': 162 | if text: 163 | module.fail_json(msg='Do not use text=true when using ' 164 | + 'cmd_type=config. The text param is only ' 165 | + 'supported when cmd_type=show') 166 | 167 | args = dict(command=command, text=text, cmd_type=cmd_type) 168 | proposed = {} 169 | changed = False 170 | for param, value in args.iteritems(): 171 | if value: 172 | proposed[param] = value 173 | 174 | if cmd_type == 'config': 175 | if isinstance(command, str): 176 | command_list = command.split(',') 177 | command = nxapi_lib.cmd_list_to_string(command_list) 178 | elif isinstance(command, list): 179 | command = nxapi_lib.cmd_list_to_string(command) 180 | 181 | if cmd_type == 'show': 182 | data_dict = xmltodict.parse(device.show(command, text=text)[1]) 183 | get_data = data_dict['ins_api']['outputs']['output'] 184 | clierror = get_data.get('clierror', None) 185 | errmsg = get_data.get('msg', None) 186 | if clierror: 187 | raise IOError(clierror, errmsg) 188 | else: 189 | body = get_data['body'] 190 | elif cmd_type == 'config': 191 | data_dict = xmltodict.parse(device.config(command)[1]) 192 | changed = True 193 | get_data = data_dict['ins_api']['outputs']['output'] 194 | try: 195 | for each in get_data: 196 | clierror = each.get('clierror', None) 197 | msg = each.get('msg', None) 198 | if clierror: 199 | raise IOError(clierror, msg) 200 | except AttributeError: 201 | clierror = get_data.get('clierror', None) 202 | msg = get_data.get('msg', None) 203 | except: 204 | return data 205 | 206 | if clierror: 207 | raise IOError(clierror, msg) 208 | else: 209 | body = get_data 210 | 211 | results = {} 212 | results['changed'] = changed 213 | results['proposed'] = proposed 214 | results['commands'] = command 215 | results['results'] = body 216 | 217 | module.exit_json(**results) 218 | 219 | from ansible.module_utils.basic import * 220 | main() 221 | -------------------------------------------------------------------------------- /library/nxos_copy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_copy 21 | short_description: Copy file from remote server to Nexus switch 22 | description: 23 | - Commands executed locally on the switch to copy a file from a 24 | remote server to a particular path/dir on the Nexus switch 25 | author: Jason Edelman (@jedelman8) 26 | notes: 27 | - This module was tested with a remote Ubuntu 14.04 machine 28 | using SCP. 29 | - Cannot override files on the switch with this module. User should 30 | ensure the file does not exist first with nxos_dir if needed or 31 | change the name of the dest file name. 32 | - While username and password are not required params, they are 33 | if you are not using the .netauth file. .netauth file is recommended 34 | as it will clean up the each task in the playbook by not requiring 35 | the username and password params for every tasks. 36 | - Using the username and password params will override the .netauth file 37 | requirements: 38 | - NX-API 1.0 39 | - NX-OS 6.1(2)I3(1) 40 | - pycsco 41 | - xmltodict 42 | options: 43 | server_host: 44 | description: 45 | - IP Address or hostname (resolvable by switch) of the remote 46 | server that has currently has the file needed 47 | required: true 48 | default: null 49 | choices: [] 50 | aliases: [] 51 | server_path: 52 | description: 53 | - Absolute path including file name 54 | required: true 55 | default: null 56 | choices: [] 57 | aliases: [] 58 | server_un: 59 | description: 60 | - Username used to login to the server from the switch 61 | required: true 62 | default: null 63 | choices: [] 64 | aliases: [] 65 | server_pw: 66 | description: 67 | - Password used to login to the server from the switch 68 | required: true 69 | default: null 70 | choices: [] 71 | aliases: [] 72 | local_path: 73 | description: 74 | - Path on the switch where the file should be stored upon transfer 75 | required: true 76 | default: null 77 | choices: [] 78 | aliases: [] 79 | vrf: 80 | description: 81 | - VRF used to source communication to the remote file server 82 | required: false 83 | default: default 84 | choices: [] 85 | aliases: [] 86 | copy_type: 87 | description: 88 | - Protocol used to copy file from remote server to switch 89 | required: false 90 | default: scp 91 | choices: ['scp'] 92 | aliases: [] 93 | host: 94 | description: 95 | - IP Address or hostname (resolvable by Ansible control host) 96 | of the target NX-API enabled switch 97 | required: true 98 | default: null 99 | choices: [] 100 | aliases: [] 101 | username: 102 | description: 103 | - Username used to login to the switch 104 | required: false 105 | default: null 106 | choices: [] 107 | aliases: [] 108 | password: 109 | description: 110 | - Password used to login to the switch 111 | required: false 112 | default: null 113 | choices: [] 114 | aliases: [] 115 | protocol: 116 | description: 117 | - Dictates connection protocol to use for NX-API 118 | required: false 119 | default: http 120 | choices: ['http', 'https'] 121 | aliases: [] 122 | ''' 123 | 124 | EXAMPLES = ''' 125 | # copy config file from server to switch 126 | - nxos_copy: 127 | server_host=192.168.200.56 128 | server_path='/home/cisco/Public/switch_config.cfg' 129 | server_un=cisco 130 | server_pw=cisco 131 | copy_type=scp 132 | local_path='bootflash:switch_config.cfg' 133 | vrf=management 134 | host={{ inventory_hostname }} 135 | 136 | ''' 137 | 138 | try: 139 | import xmltodict 140 | import socket 141 | from pycsco.nxos.device import Device 142 | from pycsco.nxos.device import Auth 143 | from pycsco.nxos.utils import nxapi_lib 144 | except ImportError as e: 145 | print '*' * 30 146 | print e 147 | print '*' * 30 148 | 149 | 150 | def main(): 151 | 152 | module = AnsibleModule( 153 | argument_spec=dict( 154 | server_host=dict(required=True), 155 | server_path=dict(required=True), 156 | server_un=dict(required=True), 157 | server_pw=dict(required=True), 158 | local_path=dict(required=True), 159 | vrf=dict(default='default'), 160 | copy_type=dict(choices=['scp'], required=True), 161 | host=dict(required=True), 162 | username=dict(type='str'), 163 | password=dict(type='str'), 164 | ), 165 | supports_check_mode=False 166 | ) 167 | 168 | auth = Auth(vendor='cisco', model='nexus') 169 | username = module.params['username'] or auth.username 170 | password = module.params['password'] or auth.password 171 | 172 | host = socket.gethostbyname(module.params['host']) 173 | server_pw = module.params['server_pw'] 174 | 175 | device = Device(ip=host, username=username, password=password, 176 | protocol=protocol) 177 | device.set_timeout = 120 178 | 179 | local_path = module.params['local_path'] 180 | if ':' in local_path: 181 | location = local_path.split(':')[0] 182 | rightmost = local_path.split(':')[-1] 183 | if '/' in rightmost: 184 | file_name = rightmost.split('/')[-1] 185 | path_list = rightmost.split('/')[:-1] 186 | path = location + ':' + '/'.join(path_list) + '/' 187 | else: 188 | file_name = rightmost 189 | path = location 190 | else: 191 | module.fail_json(msg='invalid format for path. Requires ":"' 192 | + 'Example- bootflash:scripts/my_script.py') 193 | 194 | existing_files = nxapi_lib.switch_files_list(device, path) 195 | if file_name in existing_files: 196 | module.fail_json(msg='cannot overwrite a file. change filename in ' 197 | + 'local_path param') 198 | 199 | commands = 'terminal dont-ask ; terminal password {} ; '.format(server_pw) 200 | 201 | # example: copy scp://cisco@192.168.200.17/home/cisco/abcdefg.cfg \ 202 | # bootflash:abcdefgggg.cfg vrf management 203 | commands += 'copy {copy_type}://{server_un}@'.format(**module.params) \ 204 | + '{server_host}{server_path} '.format(**module.params) \ 205 | + '{local_path} vrf {vrf}'.format(**module.params) 206 | 207 | # module.exit_json(c=commands) 208 | results = device.config(commands) 209 | resdict = xmltodict.parse(results[1])['ins_api']['outputs']['output'] 210 | 211 | results = {} 212 | results['commands'] = commands 213 | results['terminal'] = resdict 214 | 215 | module.exit_json(**results) 216 | 217 | from ansible.module_utils.basic import * 218 | main() 219 | -------------------------------------------------------------------------------- /library/nxos_dir: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_dir 21 | short_description: Manage dirs and files in the NX-OS filesystem 22 | description: 23 | - Offers ability to create and delete directories and files 24 | on a Nexus switch 25 | author: Jason Edelman (@jedelman8) 26 | requirements: 27 | - NX-API 1.0 28 | - NX-OS 6.1(2)I3(1) 29 | - pycsco 30 | notes: 31 | - state=present should not be used when path is a file 32 | - While username and password are not required params, they are 33 | if you are not using the .netauth file. .netauth file is recommended 34 | as it will clean up the each task in the playbook by not requiring 35 | the username and password params for every tasks. 36 | - Using the username and password params will override the .netauth file 37 | options: 38 | path: 39 | description: 40 | - Path (with filename if deleting file) 41 | required: true 42 | default: null 43 | choices: [] 44 | aliases: [] 45 | state: 46 | description: 47 | - Desired state of the resource / path 48 | required: true 49 | default: null 50 | choices: ['present','absent'] 51 | aliases: [] 52 | host: 53 | description: 54 | - IP Address or hostname (resolvable by Ansible control host) 55 | of the target NX-API enabled switch 56 | required: true 57 | default: null 58 | choices: [] 59 | aliases: [] 60 | username: 61 | description: 62 | - Username used to login to the switch 63 | required: false 64 | default: null 65 | choices: [] 66 | aliases: [] 67 | password: 68 | description: 69 | - Password used to login to the switch 70 | required: false 71 | default: null 72 | choices: [] 73 | aliases: [] 74 | protocol: 75 | description: 76 | - Dictates connection protocol to use for NX-API 77 | required: false 78 | default: http 79 | choices: ['http', 'https'] 80 | aliases: [] 81 | ''' 82 | 83 | EXAMPLES = ''' 84 | # Ensure directory is created on target device 85 | - nxos_dir: path='bootflash:new_config_dir' host={{ inventory_hostname }} state=present 86 | 87 | # Ensure directory is not on target device 88 | - nxos_dir: path='bootflash:new_config_dir' host={{ inventory_hostname }} state=absent 89 | 90 | # Ensure file is not on target device 91 | - nxos_dir: path='bootflash:switch_config1.cfg' host={{ inventory_hostname }} state=absent 92 | 93 | ''' 94 | 95 | try: 96 | import socket 97 | from pycsco.nxos.device import Device 98 | from pycsco.nxos.device import Auth 99 | from pycsco.nxos.utils import nxapi_lib 100 | except ImportError as e: 101 | print '*' * 30 102 | print e 103 | print '*' * 30 104 | 105 | def main(): 106 | 107 | module = AnsibleModule( 108 | argument_spec=dict( 109 | path=dict(required=True), 110 | host=dict(required=True), 111 | username=dict(type='str'), 112 | password=dict(type='str'), 113 | state=dict(options=['present', 'absent'], required=True) 114 | ), 115 | supports_check_mode=False 116 | ) 117 | 118 | auth = Auth(vendor='cisco', model='nexus') 119 | username = module.params['username'] or auth.username 120 | password = module.params['password'] or auth.password 121 | 122 | host = socket.gethostbyname(module.params['host']) 123 | state = module.params['state'] 124 | 125 | device = Device(ip=host, username=username, password=password, 126 | protocol=protocol) 127 | 128 | path = module.params['path'] 129 | valid_list = ['bootflash', 'debug', 'log', 'logflash', 'usb1', 'usb2', 130 | 'volatile'] 131 | if ':' in path: 132 | root = path.split(':')[0] 133 | if root not in valid_list: 134 | module.fail_json(msg='invalid root directory', options=valid_list, 135 | input=path) 136 | else: 137 | module.fail_json(msg='invalid format for path. Requires ":"' 138 | + 'Example- bootflash:scripts/python/') 139 | 140 | if state == 'present': 141 | change_dir = nxapi_lib.create_dir(device, path) 142 | elif state == 'absent': 143 | change_dir = nxapi_lib.delete_dir(device, path) 144 | 145 | results = {} 146 | results['path'] = path 147 | results['changed'] = change_dir 148 | 149 | module.exit_json(**results) 150 | 151 | from ansible.module_utils.basic import * 152 | main() 153 | -------------------------------------------------------------------------------- /library/nxos_feature: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_feature 21 | short_description: Manage features in NX-API enabled devices 22 | description: 23 | - Offers ability to enable and disable features in NX-OS 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - feature name must match that from the CLI 31 | - While username and password are not required params, they are 32 | if you are not using the .netauth file. .netauth file is recommended 33 | as it will clean up the each task in the playbook by not requiring 34 | the username and password params for every tasks. 35 | - Using the username and password params will override the .netauth file 36 | options: 37 | feature: 38 | description: 39 | - Name of feature 40 | required: true 41 | default: null 42 | choices: [] 43 | aliases: [] 44 | state: 45 | description: 46 | - Desired state of the feature 47 | required: true 48 | default: null 49 | choices: ['enabled','disabled'] 50 | aliases: [] 51 | host: 52 | description: 53 | - IP Address or hostname (resolvable by Ansible control host) 54 | of the target NX-API enabled switch 55 | required: true 56 | default: null 57 | choices: [] 58 | aliases: [] 59 | username: 60 | description: 61 | - Username used to login to the switch 62 | required: false 63 | default: null 64 | choices: [] 65 | aliases: [] 66 | password: 67 | description: 68 | - Password used to login to the switch 69 | required: false 70 | default: null 71 | choices: [] 72 | aliases: [] 73 | protocol: 74 | description: 75 | - Dictates connection protocol to use for NX-API 76 | required: false 77 | default: http 78 | choices: ['http', 'https'] 79 | aliases: [] 80 | ''' 81 | 82 | EXAMPLES = ''' 83 | # Ensure lacp is enabled 84 | - nxos_feature: feature=lacp state=enabled host={{ inventory_hostname }} 85 | 86 | # Ensure ospf is disabled 87 | - nxos_feature: feature=ospf state=disabled host={{ inventory_hostname }} 88 | 89 | # Ensure vpc is enabled 90 | - nxos_feature: feature=vpc state=enabled host={{ inventory_hostname }} 91 | 92 | ''' 93 | 94 | try: 95 | import socket 96 | from pycsco.nxos.device import Device 97 | from pycsco.nxos.device import Auth 98 | from pycsco.nxos.utils import nxapi_lib 99 | except ImportError as e: 100 | print '*' * 30 101 | print e 102 | print '*' * 30 103 | 104 | 105 | def main(): 106 | 107 | module = AnsibleModule( 108 | argument_spec=dict( 109 | feature=dict(type='str', required=True), 110 | state=dict(choices=['enabled', 'disabled'], required=True), 111 | host=dict(required=True), 112 | username=dict(type='str'), 113 | password=dict(type='str'), 114 | ), 115 | supports_check_mode=True 116 | ) 117 | 118 | auth = Auth(vendor='cisco', model='nexus') 119 | username = module.params['username'] or auth.username 120 | password = module.params['password'] or auth.password 121 | 122 | host = socket.gethostbyname(module.params['host']) 123 | 124 | feature = module.params['feature'].lower() 125 | state = module.params['state'].lower() 126 | 127 | device = Device(ip=host, username=username, password=password, 128 | protocol=protocol) 129 | 130 | avail_features = nxapi_lib.get_feature_list(device) 131 | if feature not in avail_features: 132 | module.fail_json( 133 | msg='Invalid feature name.', 134 | features_currently_supported=avail_features, 135 | invalid_feature=feature) 136 | 137 | existing_state = nxapi_lib.feature_enabled(device, feature) 138 | if existing_state: 139 | existstate = 'enabled' 140 | else: 141 | existstate = 'disabled' 142 | 143 | existing = dict(state=existstate) 144 | proposed = dict(state=state) 145 | 146 | changed = False 147 | cmds = '' 148 | commands = [] 149 | postrun = {} 150 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 151 | if state == 'enabled': 152 | if delta: 153 | command = nxapi_lib.get_commands_enable_feature(feature) 154 | commands.append(command) 155 | elif state == 'disabled': 156 | if delta: 157 | command = nxapi_lib.get_commands_disable_feature(feature) 158 | commands.append(command) 159 | 160 | if commands: 161 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 162 | for each in commands if each) 163 | results = {} 164 | if cmds: 165 | if module.check_mode: 166 | module.exit_json(changed=True, commands=cmds) 167 | else: 168 | changed = True 169 | device.config(cmds) 170 | existing_state = nxapi_lib.feature_enabled(device, feature) 171 | if existing_state: 172 | existstate = 'enabled' 173 | else: 174 | existstate = 'disabled' 175 | postrun = dict(state=existstate) 176 | results['new'] = postrun 177 | 178 | results['proposed'] = proposed 179 | results['existing'] = existing 180 | results['state'] = state 181 | results['commands'] = cmds 182 | results['changed'] = changed 183 | 184 | module.exit_json(**results) 185 | 186 | from ansible.module_utils.basic import * 187 | main() 188 | -------------------------------------------------------------------------------- /library/nxos_get_facts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_get_facts 21 | short_description: Gets facts about Nexus NX-API enabled switch 22 | description: 23 | - Offers ability to extract facts from device 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - While username and password are not required params, they are 31 | if you are not using the .netauth file. .netauth file is recommended 32 | as it will clean up the each task in the playbook by not requiring 33 | the username and password params for every tasks. 34 | - Using the username and password params will override the .netauth file 35 | options: 36 | detail: 37 | description: 38 | - if set to true, returns detailed statistics for interfaces 39 | equivalent to 'show interface status' 40 | required: false 41 | default: false 42 | choices: ['true','false'] 43 | aliases: [] 44 | host: 45 | description: 46 | - IP Address or hostname (resolvable by Ansible control host) 47 | of the target NX-API enabled switch 48 | required: true 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | username: 53 | description: 54 | - Username used to login to the switch 55 | required: false 56 | default: null 57 | choices: [] 58 | aliases: [] 59 | password: 60 | description: 61 | - Password used to login to the switch 62 | required: false 63 | default: null 64 | choices: [] 65 | aliases: [] 66 | protocol: 67 | description: 68 | - Dictates connection protocol to use for NX-API 69 | required: false 70 | default: http 71 | choices: ['http', 'https'] 72 | aliases: [] 73 | ''' 74 | 75 | EXAMPLES = ''' 76 | # retrieve facts 77 | - nxos_get_facts: host={{ inventory_hostname }} 78 | 79 | # retrieve facts with detailed info for interfaces (from 'show interface status') 80 | - nxos_get_facts: host={{ inventory_hostname }} detail=true 81 | 82 | ''' 83 | 84 | try: 85 | import socket 86 | from pycsco.nxos.device import Device 87 | from pycsco.nxos.device import Auth 88 | from pycsco.nxos.utils import nxapi_lib 89 | except ImportError as e: 90 | print '*' * 30 91 | print e 92 | print '*' * 30 93 | 94 | 95 | def main(): 96 | 97 | module = AnsibleModule( 98 | argument_spec=dict( 99 | detail=dict(choices=BOOLEANS, type='bool'), 100 | host=dict(required=True), 101 | username=dict(type='str'), 102 | password=dict(type='str'), 103 | ), 104 | supports_check_mode=False 105 | ) 106 | 107 | auth = Auth(vendor='cisco', model='nexus') 108 | username = module.params['username'] or auth.username 109 | password = module.params['password'] or auth.password 110 | 111 | host = socket.gethostbyname(module.params['host']) 112 | 113 | detail = module.params['detail'] 114 | 115 | device = Device(ip=host, username=username, password=password, 116 | protocol=protocol) 117 | 118 | facts = nxapi_lib.get_facts(device) 119 | 120 | if not detail: 121 | facts.pop('interfaces_detail') 122 | 123 | module.exit_json(ansible_facts=facts) 124 | 125 | from ansible.module_utils.basic import * 126 | main() 127 | -------------------------------------------------------------------------------- /library/nxos_get_interface: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_get_interface 21 | short_description: Gets details stats on a particular interface 22 | description: 23 | - Gets details stats on a particular interface 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - Equivalent to using 'show interface $INTERFACEX/Y' 31 | - While username and password are not required params, they are 32 | if you are not using the .netauth file. .netauth file is recommended 33 | as it will clean up the each task in the playbook by not requiring 34 | the username and password params for every tasks. 35 | - Using the username and password params will override the .netauth file 36 | options: 37 | interface: 38 | description: 39 | - Full name of interface, i.e. Ethernet1/1 40 | required: true 41 | default: false 42 | choices: [] 43 | aliases: [] 44 | host: 45 | description: 46 | - IP Address or hostname (resolvable by Ansible control host) 47 | of the target NX-API enabled switch 48 | required: true 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | username: 53 | description: 54 | - Username used to login to the switch 55 | required: false 56 | default: null 57 | choices: [] 58 | aliases: [] 59 | password: 60 | description: 61 | - Password used to login to the switch 62 | required: false 63 | default: null 64 | choices: [] 65 | aliases: [] 66 | protocol: 67 | description: 68 | - Dictates connection protocol to use for NX-API 69 | required: false 70 | default: http 71 | choices: ['http', 'https'] 72 | aliases: [] 73 | ''' 74 | 75 | EXAMPLES = ''' 76 | # retrieve details info and stats on an interface (from 'show interface') 77 | - nxos_get_interface: interface=Ethernet1/1 host={{ inventory_hostname }} 78 | 79 | ''' 80 | 81 | try: 82 | import socket 83 | from pycsco.nxos.device import Device 84 | from pycsco.nxos.device import Auth 85 | from pycsco.nxos.utils import nxapi_lib 86 | except ImportError as e: 87 | print '*' * 30 88 | print e 89 | print '*' * 30 90 | 91 | 92 | def main(): 93 | 94 | module = AnsibleModule( 95 | argument_spec=dict( 96 | interface=dict(required=True), 97 | host=dict(required=True), 98 | username=dict(type='str'), 99 | password=dict(type='str'), 100 | ), 101 | supports_check_mode=False 102 | ) 103 | 104 | auth = Auth(vendor='cisco', model='nexus') 105 | username = module.params['username'] or auth.username 106 | password = module.params['password'] or auth.password 107 | 108 | host = socket.gethostbyname(module.params['host']) 109 | 110 | interface = module.params['interface'].lower() 111 | device = Device(ip=host, username=username, password=password, 112 | protocol=protocol) 113 | 114 | itype = nxapi_lib.get_interface_type(interface) 115 | if itype != 'ethernet': 116 | module.fail_json(msg='module only currently supported for Ethernet ' 117 | 'interface. Use nxapi_command for others.') 118 | else: 119 | get_data = nxapi_lib.get_interface_detail(device, interface) 120 | 121 | results = {} 122 | results['interface'] = get_data 123 | 124 | module.exit_json(**results) 125 | 126 | from ansible.module_utils.basic import * 127 | main() 128 | -------------------------------------------------------------------------------- /library/nxos_get_neighbors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_get_neighbors 21 | short_description: Gets neighbor detail from a NX-API enabled switch 22 | description: 23 | - Gets CDP or LLDP information from the switch 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - While username and password are not required params, they are 31 | if you are not using the .netauth file. .netauth file is recommended 32 | as it will clean up the each task in the playbook by not requiring 33 | the username and password params for every tasks. 34 | - Using the username and password params will override the .netauth file 35 | options: 36 | type: 37 | description: 38 | - Specify neighbor protocol on how information should 39 | be gathered from switch 40 | required: true 41 | default: null 42 | choices: ['cdp','lldp'] 43 | aliases: [] 44 | host: 45 | description: 46 | - IP Address or hostname (resolvable by Ansible control host) 47 | of the target NX-API enabled switch 48 | required: true 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | username: 53 | description: 54 | - Username used to login to the switch 55 | required: false 56 | default: null 57 | choices: [] 58 | aliases: [] 59 | password: 60 | description: 61 | - Password used to login to the switch 62 | required: false 63 | default: null 64 | choices: [] 65 | aliases: [] 66 | protocol: 67 | description: 68 | - Dictates connection protocol to use for NX-API 69 | required: true 70 | default: http 71 | choices: ['http','https'] 72 | aliases: [] 73 | ''' 74 | 75 | EXAMPLES = ''' 76 | # retrieve cdp neighbors 77 | - nxos_get_neighbors: type=cdp host={{ inventory_hostname }} 78 | 79 | # retrieve lldp neighbors 80 | - nxos_get_neighbors: type=lldp host={{ inventory_hostname }} 81 | ''' 82 | 83 | try: 84 | import socket 85 | from pycsco.nxos.device import Device 86 | from pycsco.nxos.device import Auth 87 | from pycsco.nxos.utils import nxapi_lib 88 | except ImportError as e: 89 | print '*' * 30 90 | print e 91 | print '*' * 30 92 | 93 | 94 | def main(): 95 | 96 | module = AnsibleModule( 97 | argument_spec=dict( 98 | type=dict(choices=['cdp', 'lldp'], required=True), 99 | host=dict(required=True), 100 | username=dict(type='str'), 101 | password=dict(type='str'), 102 | ), 103 | supports_check_mode=False 104 | ) 105 | 106 | auth = Auth(vendor='cisco', model='nexus') 107 | username = module.params['username'] or auth.username 108 | password = module.params['password'] or auth.password 109 | 110 | host = socket.gethostbyname(module.params['host']) 111 | 112 | neigh_type = module.params['type'].lower() 113 | device = Device(ip=host, username=username, password=password, 114 | protocol=protocol) 115 | 116 | neighbors = nxapi_lib.get_neighbors(device, neigh_type) 117 | 118 | results = {} 119 | results['resource'] = neighbors 120 | 121 | module.exit_json(**results) 122 | 123 | from ansible.module_utils.basic import * 124 | main() 125 | -------------------------------------------------------------------------------- /library/nxos_hsrp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_hsrp 21 | short_description: Manages HSRP configuration on NX-API enabled devices 22 | description: 23 | - Manages HSRP configuration on NX-API enabled devices 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - HSRP feature needs to be enabled first on the system 31 | - SVIs must exist before using this module 32 | - Interface must be a L3 port before using this module 33 | - Even when md5 is selected, only UNENCRYPTED key strings are supported 34 | in this release 35 | - While username and password are not required params, they are 36 | if you are not using the .netauth file. .netauth file is recommended 37 | as it will clean up the each task in the playbook by not requiring 38 | the username and password params for every tasks. 39 | - Using the username and password params will override the .netauth file 40 | options: 41 | group: 42 | description: 43 | - hsrp group number 44 | required: true 45 | default: null 46 | choices: [] 47 | aliases: [] 48 | interface: 49 | description: 50 | - Full name of interface that is being managed for HSRP 51 | required: true 52 | default: null 53 | choices: [] 54 | aliases: [] 55 | version: 56 | description: 57 | - nxos_hsrp version 58 | required: false 59 | default: 2 60 | choices: ['1','2'] 61 | aliases: [] 62 | priority: 63 | description: 64 | - hsrp priority 65 | required: false 66 | default: null 67 | choices: [] 68 | aliases: [] 69 | vip: 70 | description: 71 | - hsrp virtual IP address 72 | required: true 73 | default: null 74 | choices: [] 75 | aliases: [] 76 | auth_string: 77 | description: 78 | - Authentication string 79 | required: false 80 | default: null 81 | choices: [] 82 | aliases: [] 83 | auth_type: 84 | description: 85 | - Authentication type 86 | required: false 87 | default: null 88 | choices: ['text','md5'] 89 | aliases: [] 90 | state: 91 | description: 92 | - Specify desired state of the resource 93 | required: true 94 | default: present 95 | choices: ['present','absent'] 96 | aliases: [] 97 | host: 98 | description: 99 | - IP Address or hostname (resolvable by Ansible control host) 100 | of the target NX-API enabled switch 101 | required: true 102 | default: null 103 | choices: [] 104 | aliases: [] 105 | username: 106 | description: 107 | - Username used to login to the switch 108 | required: false 109 | default: null 110 | choices: [] 111 | aliases: [] 112 | password: 113 | description: 114 | - Password used to login to the switch 115 | required: false 116 | default: null 117 | choices: [] 118 | aliases: [] 119 | protocol: 120 | description: 121 | - Dictates connection protocol to use for NX-API 122 | required: false 123 | default: http 124 | choices: ['http', 'https'] 125 | aliases: [] 126 | ''' 127 | 128 | EXAMPLES = ''' 129 | # ensure hsrp is configured with following params on a SVI 130 | - nxos_hsrp: group=10 vip=10.1.1.1 priority=150 interface=vlan10 preempt=enabled host={{ inventory_hostname }} 131 | 132 | # ensure hsrp is configured with following params on a SVI 133 | - nxos_hsrp: group=10 vip=10.1.1.1 priority=150 interface=vlan10 preempt=enabled host={{ inventory_hostname }} auth_type=text auth_string=CISCO 134 | 135 | # removing hsrp config for given interface, group, and vip 136 | - nxos_hsrp: group=10 interface=vlan10 vip=10.1.1.1 host={{ inventory_hostname }} state=absent 137 | 138 | ''' 139 | 140 | try: 141 | import socket 142 | from pycsco.nxos.device import Device 143 | from pycsco.nxos.device import Auth 144 | from pycsco.nxos.utils import nxapi_lib 145 | except ImportError as e: 146 | print '*' * 30 147 | print e 148 | print '*' * 30 149 | 150 | 151 | def main(): 152 | 153 | module = AnsibleModule( 154 | argument_spec=dict( 155 | group=dict(required=True, type='str'), 156 | interface=dict(required=True), 157 | version=dict(choices=['1', '2'], default='2'), 158 | priority=dict(type='str'), 159 | preempt=dict(type='str', choices=['disabled', 'enabled']), 160 | vip=dict(required=True), 161 | auth_type=dict(choices=['text', 'md5']), 162 | auth_string=dict(type='str'), 163 | state=dict(choices=['absent', 'present'], default='present'), 164 | host=dict(required=True), 165 | username=dict(type='str'), 166 | password=dict(type='str'), 167 | ), 168 | supports_check_mode=True 169 | ) 170 | 171 | auth = Auth(vendor='cisco', model='nexus') 172 | username = module.params['username'] or auth.username 173 | password = module.params['password'] or auth.password 174 | 175 | host = socket.gethostbyname(module.params['host']) 176 | 177 | interface = module.params['interface'] 178 | group = module.params['group'] 179 | version = module.params['version'] 180 | state = module.params['state'] 181 | priority = module.params['priority'] 182 | preempt = module.params['preempt'] 183 | vip = module.params['vip'] 184 | auth_type = module.params['auth_type'] 185 | auth_string = module.params['auth_string'] 186 | 187 | device = Device(ip=host, username=username, password=password, 188 | protocol=protocol) 189 | 190 | interface = interface.lower() 191 | args = dict(group=group, version=version, priority=priority, 192 | preempt=preempt, vip=vip, auth_type=auth_type, 193 | auth_string=auth_string) 194 | 195 | if not nxapi_lib.feature_enabled(device, 'hsrp'): 196 | module.fail_json(msg='HSRP feature needs to be enabled first') 197 | 198 | intf_type = nxapi_lib.get_interface_type(interface) 199 | if intf_type != 'ethernet': 200 | if nxapi_lib.is_default(device, interface) == 'DNE': 201 | module.fail_json(msg='That interface does not exist yet. Create ' 202 | + 'it first.', interface=interface) 203 | elif intf_type == 'ethernet': 204 | mode = nxapi_lib.get_interface_mode(device, interface) 205 | if mode == 'layer2': 206 | module.fail_json(msg='That interface is a layer2 port. Make it ' 207 | + 'a layer 3 port first.', interface=interface) 208 | if auth_type or auth_string: 209 | if not (auth_type and auth_string): 210 | module.fail_json(msg='When using auth parameters, you need BOTH ' 211 | + ' auth_type AND auth_string.') 212 | 213 | proposed = {} 214 | for param, value in args.iteritems(): 215 | if value: 216 | proposed[param] = value 217 | 218 | existing = nxapi_lib.get_hsrp_group(device, group, interface) 219 | # Can't use md5 encryption with hsrp v1 220 | # And BTW, we are just offering have "md5 0 key-string" not using 7 yet 221 | # this will enforce better practice. 222 | if proposed.get('auth_type', None) == 'md5': 223 | if proposed.get('version', None) == '1': 224 | module.fail_json(msg="It's recommended to use HSRP v2 " 225 | + "when auth_type=md5") 226 | if not proposed.get('version', None) and existing['version'] == '1': 227 | module.fail_json(msg="Existing HSRP version is v1. It's " 228 | + "recommended to use HSRP v2 when auth_type=md5") 229 | elif not proposed.get('auth_type', None): 230 | if proposed.get('version', None) == '1' \ 231 | and existing['auth_type'] == 'md5': 232 | module.fail_json(msg="Existing auth_type is md5. It's recommended " 233 | + " to use HSRP v2 when using md5") 234 | 235 | changed = False 236 | cmds = '' 237 | commands = [] 238 | if state == 'present': 239 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 240 | if delta: 241 | command = nxapi_lib.get_commands_config_hsrp(delta, 242 | interface, 243 | args) 244 | commands.append(command) 245 | elif state == 'absent': 246 | if existing: 247 | command = nxapi_lib.get_commands_remove_hsrp(group, interface) 248 | commands.append(command) 249 | 250 | if commands: 251 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 252 | for each in commands if each) 253 | if cmds: 254 | if module.check_mode: 255 | module.exit_json(changed=True, commands=cmds) 256 | else: 257 | changed = True 258 | device.config(cmds) 259 | 260 | results = {} 261 | results['proposed'] = proposed 262 | results['existing'] = existing 263 | results['new'] = nxapi_lib.get_hsrp_group(device, group, interface) 264 | results['state'] = state 265 | results['commands'] = cmds 266 | results['changed'] = changed 267 | 268 | module.exit_json(**results) 269 | 270 | from ansible.module_utils.basic import * 271 | main() 272 | -------------------------------------------------------------------------------- /library/nxos_ipv4_interface: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_ipv4_interface 21 | short_description: Manages L3 attributes for IPv4 interfaces 22 | description: 23 | - Manages Layer 3 attributes for IPv4 interfaces 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - Interface must already be a L3 port when using this module. Use 31 | nxos_interface to convert an interface from L2 to L3, if needed. 32 | - Logical interfaces (po, loop, svi) must be created first with the 33 | nxos_interface module prior to configuring the ipv4 address 34 | - While username and password are not required params, they are 35 | if you are not using the .netauth file. .netauth file is recommended 36 | as it will clean up the each task in the playbook by not requiring 37 | the username and password params for every tasks. 38 | - Using the username and password params will override the .netauth file 39 | options: 40 | interface: 41 | description: 42 | - Full name of interface, i.e. Ethernet1/1, vlan10 43 | required: true 44 | default: null 45 | choices: [] 46 | aliases: [] 47 | ip_addr: 48 | description: 49 | - IPv4 IP Address 50 | required: true 51 | default: null 52 | choices: [] 53 | aliases: [] 54 | mask: 55 | description: 56 | - Subnet mask for IPv4 IP Address 57 | required: true 58 | default: null 59 | choices: [] 60 | aliases: [] 61 | state: 62 | description: 63 | - Specify desired state of the resource 64 | required: true 65 | default: present 66 | choices: ['present','absent'] 67 | aliases: [] 68 | host: 69 | description: 70 | - IP Address or hostname (resolvable by Ansible control host) 71 | of the target NX-API enabled switch 72 | required: true 73 | default: null 74 | choices: [] 75 | aliases: [] 76 | username: 77 | description: 78 | - Username used to login to the switch 79 | required: false 80 | default: null 81 | choices: [] 82 | aliases: [] 83 | password: 84 | description: 85 | - Password used to login to the switch 86 | required: false 87 | default: null 88 | choices: [] 89 | aliases: [] 90 | protocol: 91 | description: 92 | - Dictates connection protocol to use for NX-API 93 | required: false 94 | default: http 95 | choices: ['http', 'https'] 96 | aliases: [] 97 | ''' 98 | 99 | EXAMPLES = ''' 100 | # Ensure Eth1/1 has an IP address of 10.1.100.2/24 101 | - nxos_ipv4_interface: interface=Ethernet1/1 ip_addr=10.1.100.2 mask=24 host={{ inventory_hostname }} state=absent 102 | 103 | # Ensure vlan10 has an IP address of 100.1.1.3/24 104 | - nxos_ipv4_interface: interface=vlan10 ip_addr=10.1.100.3 mask=22 host={{ inventory_hostname }} 105 | 106 | # Ensure vlan10 does not have an IP address 107 | - nxos_ipv4_interface: interface=vlan10 host={{ inventory_hostname }} state=absent 108 | 109 | ''' 110 | 111 | try: 112 | import socket 113 | from pycsco.nxos.device import Device 114 | from pycsco.nxos.device import Auth 115 | from pycsco.nxos.utils import nxapi_lib 116 | except ImportError as e: 117 | print '*' * 30 118 | print e 119 | print '*' * 30 120 | 121 | 122 | def main(): 123 | 124 | module = AnsibleModule( 125 | argument_spec=dict( 126 | interface=dict(required=True), 127 | ip_addr=dict(required=True), 128 | mask=dict(type='str', required=True), 129 | state=dict(default='present', choices=['present', 'absent']), 130 | host=dict(required=True), 131 | username=dict(type='str'), 132 | password=dict(type='str'), 133 | ), 134 | supports_check_mode=True 135 | ) 136 | 137 | ''' 138 | interface - full inteface name that will be 139 | managed, e.g. Ethernet1/1, Loopback10 140 | absent removes the existing IP config - no other params needed 141 | ''' 142 | results = {} 143 | auth = Auth(vendor='cisco', model='nexus') 144 | username = module.params['username'] or auth.username 145 | password = module.params['password'] or auth.password 146 | 147 | host = socket.gethostbyname(module.params['host']) 148 | 149 | ip_addr = module.params['ip_addr'] 150 | mask = module.params['mask'] 151 | interface = module.params['interface'] 152 | state = module.params['state'] 153 | 154 | device = Device(ip=host, username=username, password=password, 155 | protocol=protocol) 156 | 157 | changed = False 158 | 159 | interface = interface.lower() 160 | intf_type = nxapi_lib.get_interface_type(interface) 161 | 162 | if state == 'present': 163 | if not ip_addr and not mask: 164 | module.fail_json(msg='Both params: ip_addr and mask are required') 165 | if intf_type == 'ethernet': 166 | mode = nxapi_lib.get_interface_mode(device, interface) 167 | if mode == 'layer2': 168 | module.fail_json(msg='Ensure interface is a Layer 3 port before ' 169 | + 'configuring IP Address and mask. You can use ' 170 | + 'nxapi_interface') 171 | 172 | if intf_type != 'ethernet' and \ 173 | nxapi_lib.is_default(device, interface) == 'DNE': 174 | module.fail_json(msg='interface does not exist on switch. Verify ' 175 | + 'switch platform or create it first with ' 176 | + "nxapi_interface if it's a logical interface") 177 | 178 | proposed = dict(interface=interface, ip_addr=ip_addr, mask=mask) 179 | existing = nxapi_lib.get_ipv4_interface(device, interface) 180 | 181 | commands = [] 182 | 183 | if state == 'absent': 184 | if existing['ip_addr']: 185 | command = nxapi_lib.get_remove_ipv4_config_commands(existing, 186 | interface) 187 | commands.append(command) 188 | elif state == 'present': 189 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 190 | if existing['ip_addr'] is None or delta: 191 | command = nxapi_lib.get_config_ipv4_commands(delta, 192 | interface, 193 | existing) 194 | commands.append(command) 195 | cmds = '' 196 | if commands: 197 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 198 | for each in commands if each) 199 | if cmds: 200 | if module.check_mode: 201 | module.exit_json(changed=True, commands=cmds) 202 | else: 203 | changed = True 204 | device.config(cmds) 205 | 206 | results['proposed'] = proposed 207 | results['existing'] = existing 208 | results['new'] = nxapi_lib.get_ipv4_interface(device, interface) 209 | results['commands'] = cmds 210 | results['changed'] = changed 211 | 212 | module.exit_json(**results) 213 | 214 | from ansible.module_utils.basic import * 215 | main() 216 | -------------------------------------------------------------------------------- /library/nxos_mtu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_mtu 21 | short_description: Manages MTU settings on Nexus switch 22 | description: 23 | - Manages MTU settings on Nexus switch 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - Either sysmtu param is required or interface AND mtu params are req'd 31 | - Absent unconfigures a given MTU if that value is currently present 32 | - While username and password are not required params, they are 33 | if you are not using the .netauth file. .netauth file is recommended 34 | as it will clean up the each task in the playbook by not requiring 35 | the username and password params for every tasks. 36 | - Using the username and password params will override the .netauth file 37 | options: 38 | interface: 39 | description: 40 | - Full name of interface, i.e. Ethernet1/1 41 | required: true 42 | default: null 43 | choices: [] 44 | aliases: [] 45 | mtu: 46 | description: 47 | - MTU for a specific interface 48 | required: false 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | sysmtu: 53 | description: 54 | - System jumbo MTU 55 | required: false 56 | default: null 57 | choices: [] 58 | aliases: [] 59 | state: 60 | description: 61 | - Specify desired state of the resource 62 | required: true 63 | default: present 64 | choices: ['present','absent'] 65 | aliases: [] 66 | host: 67 | description: 68 | - IP Address or hostname (resolvable by Ansible control host) 69 | of the target NX-API enabled switch 70 | required: true 71 | default: null 72 | choices: [] 73 | aliases: [] 74 | username: 75 | description: 76 | - Username used to login to the switch 77 | required: false 78 | default: null 79 | choices: [] 80 | aliases: [] 81 | password: 82 | description: 83 | - Password used to login to the switch 84 | required: false 85 | default: null 86 | choices: [] 87 | aliases: [] 88 | protocol: 89 | description: 90 | - Dictates connection protocol to use for NX-API 91 | required: false 92 | default: http 93 | choices: ['http', 'https'] 94 | aliases: [] 95 | ''' 96 | 97 | EXAMPLES = ''' 98 | # Ensure system mtu is 9126 99 | - nxos_mtu: sysmtu=9216 host={{ inventory_hostname }} 100 | 101 | # Config mtu on Eth1/1 (routed interface) 102 | - nxos_mtu: interface=Ethernet1/1 mtu=1600 host={{ inventory_hostname }} 103 | 104 | # Config mtu on Eth1/3 (switched interface) 105 | - nxos_mtu: interface=Ethernet1/3 mtu=9216 host={{ inventory_hostname }} 106 | 107 | # Unconfigure mtu on a given interface 108 | - nxos_mtu: interface=Ethernet1/3 mtu=9216 host={{ inventory_hostname }} state=absent 109 | 110 | ''' 111 | 112 | try: 113 | import socket 114 | from pycsco.nxos.device import Device 115 | from pycsco.nxos.device import Auth 116 | from pycsco.nxos.utils import nxapi_lib 117 | except ImportError as e: 118 | print '*' * 30 119 | print e 120 | print '*' * 30 121 | 122 | 123 | def main(): 124 | 125 | module = AnsibleModule( 126 | argument_spec=dict( 127 | mtu=dict(type='str'), 128 | interface=dict(type='str'), 129 | sysmtu=dict(type='str'), 130 | state=dict(choices=['absent', 'present'], default='present'), 131 | host=dict(required=True), 132 | username=dict(type='str'), 133 | password=dict(type='str'), 134 | ), 135 | supports_check_mode=True 136 | ) 137 | 138 | auth = Auth(vendor='cisco', model='nexus') 139 | username = module.params['username'] or auth.username 140 | password = module.params['password'] or auth.password 141 | 142 | host = socket.gethostbyname(module.params['host']) 143 | 144 | interface = module.params['interface'] 145 | 146 | mtu = module.params['mtu'] 147 | sysmtu = module.params['sysmtu'] 148 | 149 | state = module.params['state'] 150 | 151 | device = Device(ip=host, username=username, password=password, 152 | protocol=protocol) 153 | 154 | args = dict(mtu=mtu, sysmtu=sysmtu) 155 | proposed = {} 156 | for param, value in args.iteritems(): 157 | if value: 158 | proposed[param] = value 159 | 160 | if sysmtu and (interface or mtu): 161 | module.fail_json(msg='Proper usage-- either just use the sysmtu param ' 162 | + 'or use interface AND mtu params') 163 | 164 | if not sysmtu and not (interface and mtu): 165 | module.fail_json(msg='To set the interface mtu size, you need both ' 166 | + 'params: interface && mtu.') 167 | 168 | if interface: 169 | interface = interface.lower() 170 | intf_type = nxapi_lib.get_interface_type(interface) 171 | if intf_type != 'ethernet': 172 | if nxapi_lib.is_default(device, interface) == 'DNE': 173 | module.fail_json(msg='Invalid interface. It does not exist ' 174 | + 'on the switch.') 175 | 176 | existing = nxapi_lib.get_mtu(device, interface) 177 | else: 178 | existing = nxapi_lib.get_system_mtu(device) 179 | 180 | if interface and mtu: 181 | if intf_type == 'loopback': 182 | module.fail_json(msg='Cannot set MTU for loopback interface.') 183 | mode = nxapi_lib.get_interface_mode(device, interface) 184 | if mode == 'layer2': 185 | if intf_type in ['ethernet', 'portchannel']: 186 | if mtu not in [existing['sysmtu'], '1500']: 187 | module.fail_json(msg='MTU on L2 interfaces can only be set' 188 | + ' to the system default (1500) or ' 189 | + 'existing sysmtu value which is ' 190 | + ' %s' % existing['sysmtu']) 191 | elif mode == 'layer3': 192 | if intf_type in ['ethernet', 'portchannel', 'svi']: 193 | if (int(mtu) < 576 or int(mtu) > 9216) \ 194 | or ((int(mtu) % 2) != 0): 195 | module.fail_json(msg='Invalid MTU for Layer 3 interface- ' 196 | + 'needs to be an even number between 576' 197 | + ' and 9216') 198 | if sysmtu: 199 | if (int(sysmtu) < 576 or int(sysmtu) > 9216) \ 200 | or ((int(sysmtu) % 2) != 0): 201 | module.fail_json(msg='Invalid MTU- needs to be an even ' 202 | + 'number between 576 and 9216') 203 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 204 | 205 | changed = False 206 | commands = [] 207 | cmds = '' 208 | if state == 'present': 209 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 210 | if delta: 211 | command = nxapi_lib.get_commands_config_mtu(dict(delta), interface) 212 | commands.append(command) 213 | elif state == 'absent': 214 | common = set(proposed.iteritems()).intersection(existing.iteritems()) 215 | if common: 216 | command = nxapi_lib.get_commands_remove_mtu(dict(common), 217 | interface) 218 | commands.append(command) 219 | 220 | if commands: 221 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 222 | for each in commands if each) 223 | if cmds: 224 | if module.check_mode: 225 | module.exit_json(changed=True, commands=cmds) 226 | else: 227 | changed = True 228 | device.config(cmds) 229 | if interface: 230 | interface = interface.lower() 231 | postrun = nxapi_lib.get_mtu(device, interface) 232 | else: 233 | postrun = nxapi_lib.get_system_mtu(device) 234 | results = {} 235 | results['proposed'] = proposed 236 | results['existing'] = existing 237 | results['new'] = postrun 238 | results['state'] = state 239 | results['commands'] = cmds 240 | results['changed'] = changed 241 | 242 | module.exit_json(**results) 243 | 244 | from ansible.module_utils.basic import * 245 | main() 246 | -------------------------------------------------------------------------------- /library/nxos_ping: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_ping 21 | short_description: Tests reachability using ping from Nexus switch 22 | description: 23 | - Tests reachability using ping from switch to a remote destination 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | - xmltodict 30 | notes: 31 | - While username and password are not required params, they are 32 | if you are not using the .netauth file. .netauth file is recommended 33 | as it will clean up the each task in the playbook by not requiring 34 | the username and password params for every tasks. 35 | - Using the username and password params will override the .netauth file 36 | options: 37 | dest: 38 | description: 39 | - IP address or hostname (resolvable by switch) of remote node 40 | required: true 41 | default: null 42 | choices: [] 43 | aliases: [] 44 | count: 45 | description: 46 | - Number of packets to send 47 | required: false 48 | default: 4 49 | choices: [] 50 | aliases: [] 51 | source: 52 | description: 53 | - Source IP Address 54 | required: false 55 | default: null 56 | choices: [] 57 | aliases: [] 58 | vrf: 59 | description: 60 | - Outgoing VRF 61 | required: false 62 | default: null 63 | choices: [] 64 | aliases: [] 65 | host: 66 | description: 67 | - IP Address or hostname (resolvable by Ansible control host) 68 | of the target NX-API enabled switch 69 | required: true 70 | default: null 71 | choices: [] 72 | aliases: [] 73 | username: 74 | description: 75 | - Username used to login to the switch 76 | required: false 77 | default: null 78 | choices: [] 79 | aliases: [] 80 | password: 81 | description: 82 | - Password used to login to the switch 83 | required: false 84 | default: null 85 | choices: [] 86 | aliases: [] 87 | protocol: 88 | description: 89 | - Dictates connection protocol to use for NX-API 90 | required: false 91 | default: http 92 | choices: ['http', 'https'] 93 | aliases: [] 94 | ''' 95 | 96 | EXAMPLES = ''' 97 | # test reachability to 8.8.8.8 using mgmt vrf 98 | - nxos_ping: dest=8.8.8.8 vrf=management host={{ inventory_hostname }} 99 | 100 | # Test reachability to a few different public IPs using mgmt vrf 101 | - nxos_ping: dest={{ item }} vrf=management host={{ inventory_hostname }} 102 | with_items: 103 | - 8.8.8.8 104 | - 4.4.4.4 105 | - 198.6.1.4 106 | 107 | ''' 108 | try: 109 | import xmltodict 110 | import socket 111 | from pycsco.nxos.device import Device 112 | from pycsco.nxos.device import Auth 113 | except ImportError as e: 114 | print '*' * 30 115 | print e 116 | print '*' * 30 117 | 118 | 119 | def get_summary(results_list, ref_point): 120 | summ_string = results_list[ref_point+1] 121 | summ_list = summ_string.split(',') 122 | pkts_tx = summ_list[0].split('packets')[0].strip() 123 | pkts_rx = summ_list[1].split('packets')[0].strip() 124 | pkt_loss = summ_list[2].split('packet')[0].strip() 125 | summary = dict(packets_tx=pkts_tx, 126 | packets_rx=pkts_rx, 127 | packet_loss=pkt_loss) 128 | 129 | return summary 130 | 131 | 132 | def get_rtt(results_list, packet_loss, location): 133 | if packet_loss != '100.00%': 134 | rtt_string = results_list[location] 135 | base = rtt_string.split('=')[1] 136 | rtt_list = base.split('/') 137 | min_rtt = rtt_list[0].lstrip() 138 | avg_rtt = rtt_list[1] 139 | max_rtt = rtt_list[2][:-3] 140 | rtt = dict(min=min_rtt, avg=avg_rtt, max=max_rtt) 141 | else: 142 | rtt = dict(min=None, avg=None, max=None) 143 | 144 | return rtt 145 | 146 | 147 | def get_statistics_summary_line(response_as_list): 148 | 149 | for each in response_as_list: 150 | if '---' in each: 151 | index = response_as_list.index(each) 152 | return index 153 | 154 | 155 | def main(): 156 | 157 | module = AnsibleModule( 158 | argument_spec=dict( 159 | dest=dict(required=True), 160 | count=dict(default=4), 161 | vrf=dict(), 162 | source=dict(), 163 | host=dict(required=True), 164 | username=dict(type='str'), 165 | password=dict(type='str'), 166 | ), 167 | supports_check_mode=False 168 | ) 169 | 170 | auth = Auth(vendor='cisco', model='nexus') 171 | username = module.params['username'] or auth.username 172 | password = module.params['password'] or auth.password 173 | 174 | host = socket.gethostbyname(module.params['host']) 175 | 176 | dest = module.params['dest'] 177 | count = module.params['count'] 178 | vrf = module.params['vrf'] 179 | source = module.params['source'] 180 | 181 | device = Device(ip=host, username=username, password=password, 182 | protocol=protocol) 183 | 184 | OPTIONS = { 185 | 'vrf': vrf, 186 | 'count': count, 187 | 'source': source 188 | } 189 | 190 | ping_string = 'ping ' + dest 191 | 192 | for command, arg in OPTIONS.iteritems(): 193 | if arg: 194 | ping_string += ' {} {}'.format(command, arg) 195 | 196 | ping_results = device.show(ping_string, text=True) 197 | ping_d = xmltodict.parse(ping_results[1]) 198 | ping = ping_d['ins_api']['outputs']['output']['body'] 199 | 200 | # convert ascii results to a list 201 | ping_results_list = ping.split('\n') 202 | 203 | # --- 8.8.8.99 ping statistics --- this line is being used as a point 204 | # of reference 205 | ref_point = get_statistics_summary_line(ping_results_list) 206 | 207 | results = {} 208 | results['command'] = ping_string 209 | results['action'] = ping_results_list[0] 210 | results['dest'] = dest 211 | # results['text_response'] = ping_results_list[1:ref_point-1] 212 | results['count'] = count 213 | results['summary'] = get_summary(ping_results_list, ref_point) 214 | results['rtt'] = get_rtt(ping_results_list, 215 | results['summary']['packet_loss'], 216 | ref_point+2) 217 | 218 | module.exit_json(**results) 219 | 220 | from ansible.module_utils.basic import * 221 | main() 222 | -------------------------------------------------------------------------------- /library/nxos_portchannel: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_portchannel 21 | short_description: Manages port-channel interfaces 22 | description: 23 | - Manages port-channel specific configuration parameters 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - Absent removes the portchannel config and interface if it 31 | already exists 32 | - Members must be a list 33 | - LACP needs to be enabled first if active/passive modes are used 34 | - While username and password are not required params, they are 35 | if you are not using the .netauth file. .netauth file is recommended 36 | as it will clean up the each task in the playbook by not requiring 37 | the username and password params for every tasks. 38 | - Using the username and password params will override the .netauth file 39 | options: 40 | group: 41 | description: 42 | - channel-group number for the port-channel 43 | required: true 44 | default: null 45 | choices: [] 46 | aliases: [] 47 | mode: 48 | description: 49 | - Mode for the port-channel, i.e. on, active, passive 50 | required: false 51 | default: on 52 | choices: ['active','passive','on'] 53 | aliases: [] 54 | min_links: 55 | description: 56 | - min links required to keep portchannel up 57 | required: false 58 | default: null 59 | choices: [] 60 | aliases: [] 61 | members: 62 | description: 63 | - List of interfaces that will be managed in a given portchannel 64 | required: false 65 | default: null 66 | choices: [] 67 | aliases: [] 68 | state: 69 | description: 70 | - Manage the state of the resource 71 | required: true 72 | default: null 73 | choices: ['present','absent'] 74 | aliases: [] 75 | host: 76 | description: 77 | - IP Address or hostname (resolvable by Ansible control host) 78 | of the target NX-API enabled switch 79 | required: true 80 | default: null 81 | choices: [] 82 | aliases: [] 83 | username: 84 | description: 85 | - Username used to login to the switch 86 | required: false 87 | default: null 88 | choices: [] 89 | aliases: [] 90 | password: 91 | description: 92 | - Password used to login to the switch 93 | required: false 94 | default: null 95 | choices: [] 96 | aliases: [] 97 | protocol: 98 | description: 99 | - Dictates connection protocol to use for NX-API 100 | required: false 101 | default: http 102 | choices: ['http', 'https'] 103 | aliases: [] 104 | ''' 105 | EXAMPLES = ''' 106 | # Ensure port-channel 99 doesn't exist on the switch 107 | - nxos_portchannel: group=99 host={{ inventory_hostname }} state=absent 108 | 109 | # Ensure port-channel99 is created, add two members, and set to mode on 110 | - nxos_portchannel: 111 | group: 99 112 | members: ['Ethernet1/1','Ethernet1/2'] 113 | mode: 'active' 114 | host: "{{ inventory_hostname }}" 115 | state: present 116 | 117 | ''' 118 | 119 | try: 120 | import socket 121 | from pycsco.nxos.device import Device 122 | from pycsco.nxos.device import Auth 123 | from pycsco.nxos.utils import nxapi_lib 124 | except ImportError as e: 125 | print '*' * 30 126 | print e 127 | print '*' * 30 128 | 129 | 130 | def main(): 131 | 132 | module = AnsibleModule( 133 | argument_spec=dict( 134 | group=dict(required=True, type='str'), 135 | mode=dict(choices=['on', 'active', 'passive'], 136 | default='on', 137 | type='str'), 138 | min_links=dict(default=None, type='str'), 139 | members=dict(default=None), 140 | state=dict(choices=['absent', 'present'], 141 | default='present'), 142 | host=dict(required=True), 143 | username=dict(type='str'), 144 | password=dict(type='str'), 145 | ), 146 | supports_check_mode=True 147 | ) 148 | 149 | # ************************************************************************ 150 | # members must be a list & this requires using YAML syntax in the playbook 151 | # ************************************************************************ 152 | 153 | results = {} 154 | auth = Auth(vendor='cisco', model='nexus') 155 | username = module.params['username'] or auth.username 156 | password = module.params['password'] or auth.password 157 | 158 | host = socket.gethostbyname(module.params['host']) 159 | 160 | group = module.params['group'] 161 | mode = module.params['mode'] 162 | min_links = module.params['min_links'] 163 | members = module.params['members'] 164 | 165 | state = module.params['state'] 166 | 167 | device = Device(ip=host, username=username, password=password, 168 | protocol=protocol) 169 | if mode in ['active', 'passive']: 170 | if not nxapi_lib.feature_enabled(device, 'lacp'): 171 | module.fail_json(msg='LACP feature needs to be enabled first') 172 | 173 | changed = False 174 | 175 | args = dict(group=group, mode=mode, min_links=min_links, members=members) 176 | 177 | proposed = {} 178 | for param, value in args.iteritems(): 179 | if value: 180 | proposed[param] = value 181 | 182 | existing = nxapi_lib.get_portchannel(device, group) 183 | 184 | commands = [] 185 | changed = False 186 | active_portchannels = nxapi_lib.get_portchannel_list(device) 187 | if state == 'absent': 188 | if existing: 189 | command = nxapi_lib.get_commands_to_remove_portchannel(device, 190 | group) 191 | commands.append(command) 192 | elif state == 'present': 193 | if group not in active_portchannels: 194 | command = nxapi_lib.config_portchannel(proposed, mode, group) 195 | commands.append(command) 196 | 197 | elif existing and group in active_portchannels: 198 | command = nxapi_lib.get_commands_to_remove_members(proposed, 199 | existing) 200 | commands.append(command) 201 | 202 | command = nxapi_lib.get_commands_to_add_members(proposed, existing) 203 | commands.append(command) 204 | 205 | command = nxapi_lib.get_commands_if_mode_change(proposed, 206 | existing, 207 | group, 208 | mode) 209 | # Needs to be first in the list in order to allow for port-channel 210 | # to be removed, re-added with new mode, and so more members 211 | # can be added in the same Ansible task 212 | commands.insert(0, command) 213 | 214 | if min_links: 215 | command = get_commands_min_links(existing, 216 | proposed, 217 | group, 218 | min_links) 219 | commands.append(command) 220 | cmds = '' 221 | if commands: 222 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 223 | for each in commands if each) 224 | 225 | if cmds: 226 | if module.check_mode: 227 | module.exit_json(changed=True, commands=cmds) 228 | else: 229 | changed = True 230 | device.config(cmds) 231 | 232 | results['proposed'] = proposed 233 | results['existing'] = existing 234 | results['new'] = nxapi_lib.get_portchannel(device, group) 235 | results['state'] = state 236 | results['commands'] = cmds 237 | results['changed'] = changed 238 | 239 | module.exit_json(**results) 240 | 241 | from ansible.module_utils.basic import * 242 | main() 243 | -------------------------------------------------------------------------------- /library/nxos_save_config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_save_config 21 | short_description: Saves running configuration 22 | description: 23 | - Saves running config to startup-config or file of your choice 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | - xmltodict 30 | notes: 31 | - While username and password are not required params, they are 32 | if you are not using the .netauth file. .netauth file is recommended 33 | as it will clean up the each task in the playbook by not requiring 34 | the username and password params for every tasks. 35 | - Using the username and password params will override the .netauth file 36 | options: 37 | path: 38 | description: 39 | - Path of destination. Ex: bootflash:config.cfg, etc. 40 | required: false 41 | default: null 42 | choices: [] 43 | aliases: [] 44 | host: 45 | description: 46 | - IP Address or hostname (resolvable by Ansible control host) 47 | of the target NX-API enabled switch 48 | required: true 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | username: 53 | description: 54 | - Username used to login to the switch 55 | required: false 56 | default: null 57 | choices: [] 58 | aliases: [] 59 | password: 60 | description: 61 | - Password used to login to the switch 62 | required: false 63 | default: null 64 | choices: [] 65 | aliases: [] 66 | protocol: 67 | description: 68 | - Dictates connection protocol to use for NX-API 69 | required: false 70 | default: http 71 | choices: ['http', 'https'] 72 | aliases: [] 73 | ''' 74 | 75 | EXAMPLES = ''' 76 | # save running config to startup-config 77 | - nxos_save_config: host={{ inventory_hostname }} 78 | 79 | # save running config to dir in bootflash 80 | - nxos_save_config: path='bootflash:configs/my_config.cfg' host={{ inventory_hostname }} 81 | 82 | ''' 83 | try: 84 | import xmltodict 85 | import socket 86 | from pycsco.nxos.device import Device 87 | from pycsco.nxos.device import Auth 88 | except ImportError as e: 89 | print '*' * 30 90 | print e 91 | print '*' * 30 92 | 93 | 94 | def main(): 95 | 96 | module = AnsibleModule( 97 | argument_spec=dict( 98 | path=dict(default='startup-config'), 99 | host=dict(required=True), 100 | username=dict(type='str'), 101 | password=dict(type='str'), 102 | ), 103 | supports_check_mode=False 104 | ) 105 | 106 | auth = Auth(vendor='cisco', model='nexus') 107 | username = module.params['username'] or auth.username 108 | password = module.params['password'] or auth.password 109 | 110 | host = socket.gethostbyname(module.params['host']) 111 | 112 | path = module.params['path'] 113 | 114 | device = Device(ip=host, username=username, password=password, 115 | protocol=protocol) 116 | 117 | if path != 'startup-config': 118 | if ':' not in path: 119 | module.fail_json(msg='invalid format for path. Requires ":"' 120 | + 'Example- bootflash:config.cfg ' 121 | + 'or bootflash:/configs/test.cfg') 122 | 123 | command = 'copy run ' + path 124 | error = None 125 | changed = False 126 | try: 127 | save_config = device.show(command, text=True) 128 | dict_results = xmltodict.parse(save_config[1]) 129 | save = dict_results['ins_api']['outputs']['output']['body'] 130 | # convert ascii results to a list 131 | save_config_list = save.split('\n') 132 | complete = False 133 | for each in save_config_list: 134 | if '100%' in each or 'copy complete' in each.lower(): 135 | complete = True 136 | changed = True 137 | 138 | if complete: 139 | result = 'successful' 140 | else: 141 | error = 'error: could not validate save' 142 | except KeyError: 143 | error = dict_results['ins_api']['outputs']['output'].get( 144 | 'clierror', 'error3: could not validate save') 145 | except: 146 | error = 'error2: could not validate save' 147 | 148 | if error is not None: 149 | module.fail_json(msg=error) 150 | 151 | results = {} 152 | results['path'] = path 153 | results['save'] = result 154 | results['changed'] = changed 155 | 156 | module.exit_json(**results) 157 | 158 | from ansible.module_utils.basic import * 159 | main() 160 | -------------------------------------------------------------------------------- /library/nxos_switchport: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_switchport 21 | short_description: Manages Layer 2 switchport interfaces 22 | description: 23 | - Manages Layer 2 interfaces 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - Interface must be a Layer2 port already. If not, convert to L2 with 31 | nxos_interface module 32 | - When state=absent, if the switchport does not have a default config, 33 | it is set back to a default config from a vlan configuration perspective. 34 | This means, if state=absent, the resulting interface config will be an 35 | access port with vlan 1 configured as an access vlan even if the existing 36 | config is a trunk port. 37 | - Access and Native VLANs are required to exist on the switch before 38 | configuring them with this module 39 | - While username and password are not required params, they are 40 | if you are not using the .netauth file. .netauth file is recommended 41 | as it will clean up the each task in the playbook by not requiring 42 | the username and password params for every tasks. 43 | - Using the username and password params will override the .netauth file 44 | options: 45 | interface: 46 | description: 47 | - Full name of the interface, i.e. Ethernet1/1 48 | required: true 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | mode: 53 | description: 54 | - Mode for the Layer 2 port 55 | required: true 56 | default: null 57 | choices: ['access','trunk'] 58 | aliases: [] 59 | access_vlan: 60 | description: 61 | - if mode=access, used as the access vlan id 62 | required: false 63 | default: 1 64 | choices: [] 65 | aliases: [] 66 | native_vlan: 67 | description: 68 | - if mode=trunk, used as the trunk native vlan id 69 | required: false 70 | default: 1 71 | choices: [] 72 | aliases: [] 73 | trunk_vlans: 74 | description: 75 | - if mode=trunk, used as the vlan range to carry over trunk 76 | required: false 77 | default: '1-4094' 78 | choices: [] 79 | aliases: [] 80 | state: 81 | description: 82 | - Manage the state of the resource 83 | required: true 84 | default: null 85 | choices: ['present','absent'] 86 | aliases: [] 87 | host: 88 | description: 89 | - IP Address or hostname (resolvable by Ansible control host) 90 | of the target NX-API enabled switch 91 | required: true 92 | default: null 93 | choices: [] 94 | aliases: [] 95 | username: 96 | description: 97 | - Username used to login to the switch 98 | required: false 99 | default: null 100 | choices: [] 101 | aliases: [] 102 | password: 103 | description: 104 | - Password used to login to the switch 105 | required: false 106 | default: null 107 | choices: [] 108 | aliases: [] 109 | protocol: 110 | description: 111 | - Dictates connection protocol to use for NX-API 112 | required: false 113 | default: http 114 | choices: ['http', 'https'] 115 | aliases: [] 116 | ''' 117 | EXAMPLES = ''' 118 | # Config a switchport to be a trunk interface with native vlan 10 and carriers vlan 2-100 119 | - nxos_switchport: interface=Ethernet1/1 mode=trunk native_vlan=10 trunk_vlans=2-100 host={{ inventory_hostname }} 120 | 121 | # Config a switchport to an access port on vlan 20 122 | - nxos_switchport: interface=Ethernet1/2 mode=access access_vlan=20 host={{ inventory_hostname }} 123 | 124 | # Remove existing access port vlan configuration on a switchport (mode is required) 125 | - nxos_switchport: interface=Ethernet1/2 host={{ inventory_hostname }} mode=access state=absent 126 | 127 | # Remove existing trunk port vlan configuration on a switchport (mode is required) 128 | - nxos_switchport: interface=Ethernet1/1 host={{ inventory_hostname }} mode=trunk state=absent 129 | ''' 130 | 131 | try: 132 | import socket 133 | from pycsco.nxos.device import Device 134 | from pycsco.nxos.device import Auth 135 | from pycsco.nxos.utils import nxapi_lib 136 | except ImportError as e: 137 | print '*' * 30 138 | print e 139 | print '*' * 30 140 | 141 | 142 | def main(): 143 | 144 | module = AnsibleModule( 145 | argument_spec=dict( 146 | interface=dict(required=True, type='str'), 147 | mode=dict(required=True, choices=['access', 'trunk']), 148 | access_vlan=dict(type='str', default='1'), 149 | native_vlan=dict(type='str', default='1'), 150 | trunk_vlans=dict(type='str', default='1-4094'), 151 | state=dict(choices=['absent', 'present'], 152 | default='present'), 153 | host=dict(required=True), 154 | username=dict(default='cisco'), 155 | password=dict(default='!cisco123!'), 156 | ), 157 | supports_check_mode=True 158 | ) 159 | 160 | auth = Auth(vendor='cisco', model='nexus') 161 | username = module.params['username'] or auth.username 162 | password = module.params['password'] or auth.password 163 | 164 | host = socket.gethostbyname(module.params['host']) 165 | 166 | interface = module.params['interface'] 167 | mode = module.params['mode'] 168 | access_vlan = module.params['access_vlan'] 169 | state = module.params['state'] 170 | trunk_vlans = module.params['trunk_vlans'] 171 | native_vlan = module.params['native_vlan'] 172 | 173 | device = Device(ip=host, username=username, password=password, 174 | protocol=protocol) 175 | 176 | args = dict(interface=interface, mode=mode, access_vlan=access_vlan, 177 | native_vlan=native_vlan, trunk_vlans=trunk_vlans) 178 | 179 | interface = interface.lower() 180 | 181 | if mode == 'access': 182 | # Requring the user to configure an access_vlan if configuring an 183 | # access port 184 | # even if they want the default VLAN 1 185 | if not access_vlan and state == 'present': 186 | module.fail_json(msg='access_vlan param is required when ' 187 | + 'mode=access && state=present') 188 | 189 | current_mode = nxapi_lib.get_interface_mode(device, interface) 190 | 191 | # Current mode will return layer3, layer2, or unknown 192 | if current_mode == 'unknown' or current_mode == 'layer3': 193 | module.fail_json(msg='Ensure interface is configured to be a L2 port ' 194 | + 'first before using this module.') 195 | 196 | # existing will never be null for Ethernet interface as there is always a 197 | # default vlan config 198 | existing = nxapi_lib.get_switchport(device, interface) 199 | 200 | # Safeguard check to ensure the user knows what the current state of the 201 | # interface is when they are trying to remove a config. Requires user to 202 | # know if the port is currently an access port or trunk port before 203 | # removing the config 204 | if not existing: 205 | module.fail_json(msg='Make sure you are using the FULL interface name') 206 | 207 | if (existing['mode'] != args['mode']) and state == 'absent': 208 | module.fail_json(msg='You are trying to remove: ' 209 | + '{} port config, but the '.format(args['mode']) 210 | + 'port is: {} config.'.format(existing['mode']) 211 | + ' Get your act together.') 212 | 213 | current_vlans = nxapi_lib.get_list_of_vlans(device) 214 | 215 | if mode == 'access': 216 | args.pop('native_vlan') 217 | args.pop('trunk_vlans') 218 | if state == 'absent': 219 | # only removing if state = 'absent' because these are used for 220 | # config clean up if state == present 221 | existing.pop('native_vlan') 222 | existing.pop('trunk_vlans') 223 | if access_vlan not in current_vlans: 224 | module.fail_json(msg='You are trying to config or remove a VLAN ' 225 | + 'on an interface that does not exist on the ' 226 | + 'switch yet!', vlan=access_vlan) 227 | 228 | elif mode == 'trunk': 229 | args.pop('access_vlan') 230 | if state == 'absent': 231 | # only removing if state = 'absent' because these are used for 232 | # config clean up if state == present 233 | existing.pop('access_vlan') 234 | if native_vlan not in current_vlans: 235 | module.fail_json(msg='Your native vlan does not exist yet on the ' 236 | + 'switch yet. Add it first, please.', 237 | vlan=native_vlan) 238 | 239 | trunk_vlans_list = nxapi_lib.vlan_range_to_list(trunk_vlans) 240 | trunk_vlans_list.sort() 241 | 242 | if trunk_vlans and mode == 'trunk': 243 | # expanding the trunk_vlans into a list 244 | existing_trunks_list = nxapi_lib.vlan_range_to_list( 245 | (existing['trunk_vlans']) 246 | ) 247 | existing_trunks_list.sort() 248 | if trunk_vlans_list == existing_trunks_list: 249 | args['trunk_vlans'] = existing['trunk_vlans'] 250 | 251 | proposed = {} 252 | changed = False 253 | 254 | for param, value in args.iteritems(): 255 | if value: 256 | # cleaning up proposed after popping unneeded k/v pairs 257 | proposed[param] = value 258 | 259 | commands = [] 260 | 261 | if state == 'absent': 262 | if not nxapi_lib.is_switchport_default(existing): 263 | command = nxapi_lib.remove_switchport_config(device, 264 | existing, 265 | interface) 266 | commands.append(command) 267 | elif nxapi_lib.is_switchport_default(existing) and mode == 'trunk': 268 | command = ['interface ' + interface, 'no switchport mode trunk'] 269 | commands.append(command) 270 | elif state == 'present': 271 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 272 | if existing: 273 | if delta: 274 | if nxapi_lib.interface_is_portchannel(device, interface): 275 | module.fail_json(msg='Cannot change L2 config on physical ' 276 | + 'port because it is in a portchannel. ' 277 | + 'You should update portchannel config.') 278 | command = nxapi_lib.get_switchport_config_commands(device, 279 | delta, 280 | interface) 281 | commands.append(command) 282 | command = nxapi_lib.clean_up_interface_vlan_configs(proposed, 283 | existing) 284 | if command: 285 | commands.append(command) 286 | 287 | cmds = '' 288 | if commands: 289 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 290 | for each in commands if each) 291 | 292 | if cmds: 293 | if module.check_mode: 294 | module.exit_json(changed=True, commands=cmds) 295 | else: 296 | changed = True 297 | device.config(cmds) 298 | 299 | results = {} 300 | results['proposed'] = proposed 301 | results['existing'] = existing 302 | results['new'] = nxapi_lib.get_switchport(device, interface) 303 | results['state'] = state 304 | results['commands'] = cmds 305 | results['changed'] = changed 306 | 307 | module.exit_json(**results) 308 | 309 | from ansible.module_utils.basic import * 310 | main() 311 | -------------------------------------------------------------------------------- /library/nxos_udld: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | 20 | module: nxos_udld 21 | short_description: Manages UDLD global configuration params 22 | description: 23 | - Manages UDLD global configuration params 24 | author: Jason Edelman (@jedelman8) 25 | requirements: 26 | - NX-API 1.0 27 | - NX-OS 6.1(2)I3(1) 28 | - pycsco 29 | notes: 30 | - When state=absent, it unconfigures existing setings if 31 | they already exist on the switch. It is cleaner to always use 32 | state=present. 33 | - Module will fail if the udld feature has not been previously enabled 34 | - While username and password are not required params, they are 35 | if you are not using the .netauth file. .netauth file is recommended 36 | as it will clean up the each task in the playbook by not requiring 37 | the username and password params for every tasks. 38 | - Using the username and password params will override the .netauth file 39 | options: 40 | aggressive: 41 | description: 42 | - Toggles aggressive mode 43 | required: false 44 | default: null 45 | choices: ['enabled','disabled'] 46 | aliases: [] 47 | msg_time: 48 | description: 49 | - Message time in seconds for UDLD packets 50 | required: false 51 | default: null 52 | choices: [] 53 | aliases: [] 54 | reset: 55 | description: 56 | - Ability to reset UDLD down interfaces 57 | required: false 58 | default: null 59 | choices: ['true','false'] 60 | aliases: [] 61 | state: 62 | description: 63 | - Manage the state of the resource 64 | required: true 65 | default: present 66 | choices: ['present','absent'] 67 | aliases: [] 68 | host: 69 | description: 70 | - IP Address or hostname (resolvable by Ansible control host) 71 | of the target NX-API enabled switch 72 | required: true 73 | default: null 74 | choices: [] 75 | aliases: [] 76 | username: 77 | description: 78 | - Username used to login to the switch 79 | required: false 80 | default: null 81 | choices: [] 82 | aliases: [] 83 | password: 84 | description: 85 | - Password used to login to the switch 86 | required: false 87 | default: null 88 | choices: [] 89 | aliases: [] 90 | protocol: 91 | description: 92 | - Dictates connection protocol to use for NX-API 93 | required: false 94 | default: http 95 | choices: ['http', 'https'] 96 | aliases: [] 97 | ''' 98 | EXAMPLES = ''' 99 | # ensure udld aggressive mode is globally disabled and se global message interval is 20 100 | - nxos_udld: aggressive=disabled msg_time=20 host={{ inventory_hostname }} 101 | 102 | # Ensure agg mode is globally enabled and msg time is 15 103 | - nxos_udld: aggressive=enabled msg_time=15 host={{ inventory_hostname }} state=present 104 | 105 | # Ensure msg_time is unconfigured (if it is already 25- basically defaults back to 15 anyway) 106 | 107 | ''' 108 | try: 109 | import socket 110 | from pycsco.nxos.device import Device 111 | from pycsco.nxos.device import Auth 112 | from pycsco.nxos.utils import nxapi_lib 113 | except ImportError as e: 114 | print '*' * 30 115 | print e 116 | print '*' * 30 117 | 118 | 119 | def main(): 120 | 121 | module = AnsibleModule( 122 | argument_spec=dict( 123 | aggressive=dict(choices=['enabled', 'disabled']), 124 | msg_time=dict(type='str'), 125 | reset=dict(choices=BOOLEANS, type='bool'), 126 | state=dict(choices=['absent', 'present'], default='present'), 127 | host=dict(required=True), 128 | username=dict(type='str'), 129 | password=dict(type='str'), 130 | ), 131 | supports_check_mode=True 132 | ) 133 | 134 | auth = Auth(vendor='cisco', model='nexus') 135 | username = module.params['username'] or auth.username 136 | password = module.params['password'] or auth.password 137 | 138 | host = socket.gethostbyname(module.params['host']) 139 | 140 | aggressive = module.params['aggressive'] 141 | msg_time = module.params['msg_time'] 142 | reset = module.params['reset'] 143 | state = module.params['state'] 144 | 145 | device = Device(ip=host, username=username, password=password, 146 | protocol=protocol) 147 | 148 | args = dict(aggressive=aggressive, msg_time=msg_time, reset=reset) 149 | ''' 150 | in UDLD INTERFACE, make sure it's an ETH interface, ohterwise faiL 151 | nxapi_udld_interface: mode=enabled/disabled/agressive 152 | ''' 153 | proposed = {} 154 | if not nxapi_lib.feature_enabled(device, 'udld'): 155 | module.fail_json(msg='UDLD feature needs to be enabled first') 156 | 157 | if aggressive and state == 'absent': 158 | module.fail_json(msg="It's better to use state=present when " 159 | + "configuring or unconfiguring agg mode. " 160 | + "state=absent is just for when using " 161 | + "msg_time param.") 162 | for param, value in args.iteritems(): 163 | if value: 164 | proposed[param] = value 165 | 166 | existing = nxapi_lib.get_udld_global(device) 167 | 168 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 169 | 170 | changed = False 171 | commands = [] 172 | 173 | if state == 'present': 174 | 175 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 176 | if delta: 177 | command = nxapi_lib.get_commands_config_udld_global(dict(delta)) 178 | commands.append(command) 179 | elif state == 'absent': 180 | common = set(proposed.iteritems()).intersection(existing.iteritems()) 181 | if common: 182 | command = nxapi_lib.get_commands_remove_udld_global(dict(common)) 183 | commands.append(command) 184 | cmds = '' 185 | if commands: 186 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 187 | for each in commands if each) 188 | 189 | if cmds: 190 | if module.check_mode: 191 | module.exit_json(changed=True, commands=cmds) 192 | else: 193 | changed = True 194 | device.config(cmds) 195 | 196 | results = {} 197 | results['proposed'] = proposed 198 | results['existing'] = existing 199 | results['new'] = nxapi_lib.get_udld_global(device) 200 | results['state'] = state 201 | results['commands'] = cmds 202 | results['changed'] = changed 203 | 204 | module.exit_json(**results) 205 | 206 | from ansible.module_utils.basic import * 207 | main() 208 | -------------------------------------------------------------------------------- /library/nxos_udld_interface: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: nxos_udld_interface 20 | short_description: Manages UDLD interface configuration params 21 | description: 22 | - Manages UDLD interface configuration params 23 | author: Jason Edelman (@jedelman8) 24 | requirements: 25 | - NX-API 1.0 26 | - NX-OS 6.1(2)I3(1) 27 | - pycsco 28 | notes: 29 | - When state=absent, it unconfigures existing setings if 30 | they already exist on the switch. It is much cleaner to use 31 | state=present for all options. 32 | - While username and password are not required params, they are 33 | if you are not using the .netauth file. .netauth file is recommended 34 | as it will clean up the each task in the playbook by not requiring 35 | the username and password params for every tasks. 36 | - Using the username and password params will override the .netauth file 37 | options: 38 | mode: 39 | description: 40 | - Manages udld mode for an interface 41 | required: true 42 | default: null 43 | choices: ['enabled','disabled','aggressive'] 44 | aliases: [] 45 | interface: 46 | description: 47 | - FULL name of the interface, i.e. Ethernet1/1 48 | required: true 49 | default: null 50 | choices: [] 51 | aliases: [] 52 | state: 53 | description: 54 | - Manage the state of the resource 55 | required: true 56 | default: present 57 | choices: ['present','absent'] 58 | aliases: [] 59 | host: 60 | description: 61 | - IP Address or hostname (resolvable by Ansible control host) 62 | of the target NX-API enabled switch 63 | required: true 64 | default: null 65 | choices: [] 66 | aliases: [] 67 | username: 68 | description: 69 | - Username used to login to the switch 70 | required: false 71 | default: null 72 | choices: [] 73 | aliases: [] 74 | password: 75 | description: 76 | - Password used to login to the switch 77 | required: false 78 | default: null 79 | choices: [] 80 | aliases: [] 81 | protocol: 82 | description: 83 | - Dictates connection protocol to use for NX-API 84 | required: false 85 | default: http 86 | choices: ['http', 'https'] 87 | aliases: [] 88 | ''' 89 | EXAMPLES = ''' 90 | # ensure Ethernet1/1 is configured to be in aggressive mode 91 | - nxos_udld_interface: interface=Ethernet1/1 mode=aggressive state=present host={{ inventory_hostname }} 92 | 93 | # Remove the aggressive config only if it's currently in aggressive mode and then disable udld (switch default) 94 | - nxos_udld_interface: interface=Ethernet1/1 mode=aggressive state=absent host={{ inventory_hostname }} 95 | 96 | # ensure Ethernet1/1 has aggressive mode enabled 97 | - nxos_udld_interface: interface=Ethernet1/1 mode=enabled host={{ inventory_hostname }} 98 | 99 | # ensure Ethernet1/1 has aggressive mode disabled 100 | 101 | ''' 102 | try: 103 | import socket 104 | from pycsco.nxos.device import Device 105 | from pycsco.nxos.device import Auth 106 | from pycsco.nxos.utils import nxapi_lib 107 | except ImportError as e: 108 | print '*' * 30 109 | print e 110 | print '*' * 30 111 | 112 | 113 | def main(): 114 | 115 | module = AnsibleModule( 116 | argument_spec=dict( 117 | mode=dict(choices=['enabled', 'disabled', 'aggressive'], 118 | required=True), 119 | interface=dict(type='str'), 120 | state=dict(choices=['absent', 'present'], default='present'), 121 | host=dict(required=True), 122 | username=dict(type='str'), 123 | password=dict(type='str'), 124 | ), 125 | supports_check_mode=True 126 | ) 127 | 128 | auth = Auth(vendor='cisco', model='nexus') 129 | username = module.params['username'] or auth.username 130 | password = module.params['password'] or auth.password 131 | 132 | host = socket.gethostbyname(module.params['host']) 133 | 134 | interface = module.params['interface'].lower() 135 | mode = module.params['mode'] 136 | state = module.params['state'] 137 | 138 | device = Device(ip=host, username=username, password=password, 139 | protocol=protocol) 140 | 141 | proposed = dict(mode=mode) 142 | if not nxapi_lib.feature_enabled(device, 'udld'): 143 | module.fail_json(msg='UDLD feature needs to be enabled first') 144 | 145 | existing = nxapi_lib.get_udld_interface(device, interface) 146 | 147 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 148 | 149 | changed = False 150 | commands = [] 151 | if state == 'present': 152 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 153 | if delta: 154 | command = nxapi_lib.get_commands_config_udld_interface(dict(delta), 155 | interface, 156 | device, 157 | existing) 158 | commands.append(command) 159 | elif state == 'absent': 160 | common = set(proposed.iteritems()).intersection(existing.iteritems()) 161 | if common: 162 | command = nxapi_lib.get_commands_remove_udld_interface( 163 | dict(common), interface, device, existing 164 | ) 165 | commands.append(command) 166 | cmds = '' 167 | if commands: 168 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 169 | for each in commands if each) 170 | 171 | if cmds: 172 | if module.check_mode: 173 | module.exit_json(changed=True, commands=cmds) 174 | else: 175 | changed = True 176 | device.config(cmds) 177 | 178 | results = {} 179 | results['proposed'] = proposed 180 | results['existing'] = existing 181 | results['new'] = nxapi_lib.get_udld_interface(device, interface) 182 | results['state'] = state 183 | results['commands'] = cmds 184 | results['changed'] = changed 185 | 186 | module.exit_json(**results) 187 | 188 | from ansible.module_utils.basic import * 189 | main() 190 | -------------------------------------------------------------------------------- /library/nxos_vlan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: nxos_vlan 20 | short_description: Manages VLAN resources and attributes 21 | description: 22 | - Manages VLAN configurations on NX-API enabled switches 23 | author: Jason Edelman (@jedelman8) 24 | requirements: 25 | - NX-API 1.0 26 | - NX-OS 6.1(2)I3(1) 27 | - pycsco 28 | notes: 29 | - While username and password are not required params, they are 30 | if you are not using the .netauth file. .netauth file is recommended 31 | as it will clean up the each task in the playbook by not requiring 32 | the username and password params for every tasks. 33 | - Using the username and password params will override the .netauth file 34 | options: 35 | vlan_id: 36 | description: 37 | - vlan id or range of VLANs 38 | required: true 39 | default: null 40 | choices: [] 41 | aliases: [] 42 | name: 43 | description: 44 | - name of VLAN (not supported when using range of VLANs) 45 | required: false 46 | default: null 47 | choices: [] 48 | aliases: [] 49 | vlan_state: 50 | description: 51 | - Manage the vlan oper state of the VLAN (equiv to state {active | suspend} command 52 | required: false 53 | default: active 54 | choices: ['active','suspend'] 55 | aliases: [] 56 | admin_state: 57 | description: 58 | - Manage the vlan admin state of the VLAN (equiv to shut/no shut in vlan config mode 59 | required: false 60 | default: up 61 | choices: ['up','down'] 62 | aliases: [] 63 | state: 64 | description: 65 | - Manage the state of the resource 66 | required: true 67 | default: present 68 | choices: ['present','absent'] 69 | aliases: [] 70 | host: 71 | description: 72 | - IP Address or hostname (resolvable by Ansible control host) 73 | of the target NX-API enabled switch 74 | required: true 75 | default: null 76 | choices: [] 77 | aliases: [] 78 | username: 79 | description: 80 | - Username used to login to the switch 81 | required: false 82 | default: null 83 | choices: [] 84 | aliases: [] 85 | password: 86 | description: 87 | - Password used to login to the switch 88 | required: false 89 | default: null 90 | choices: [] 91 | aliases: [] 92 | protocol: 93 | description: 94 | - Dictates connection protocol to use for NX-API 95 | required: false 96 | default: http 97 | choices: ['http', 'https'] 98 | aliases: [] 99 | ''' 100 | EXAMPLES = ''' 101 | # Ensure VLAN 50 exists with the name WEB and is in the shutdown state 102 | - nxos_vlan: vlan_id=50 host={{ inventory_hostname }} admin_state=down name=WEB 103 | 104 | # Ensure VLAN is NOT on the device 105 | - nxos_vlan: vlan_id=50 host={{ inventory_hostname }} state=absent 106 | 107 | # Ensure a range of VLANs are present on the switch 108 | - nxos_vlan: vlan_id="2-10,20,50,55-60" host={{ inventory_hostname }} state=present 109 | 110 | # Ensure a group of VLANs are present with the given names 111 | - nxos_vlan: vlan_id={{ item.vlan_id }} name={{ item.name }} host={{ inventory_hostname }} state=present 112 | with_items: 113 | - vlan_id: 10 114 | name: web 115 | - vlan_id: 20 116 | name: app 117 | - { vlan_id: 30, name: db } 118 | - vlan_id: 40 119 | name: misc 120 | - vlan_id: 99 121 | name: native_vlan 122 | ''' 123 | 124 | try: 125 | import socket 126 | from pycsco.nxos.device import Device 127 | from pycsco.nxos.device import Auth 128 | from pycsco.nxos.utils import nxapi_lib 129 | except ImportError as e: 130 | print '*' * 30 131 | print e 132 | print '*' * 30 133 | 134 | 135 | def main(): 136 | 137 | module = AnsibleModule( 138 | argument_spec=dict( 139 | vlan_id=dict(required=True, type='str'), 140 | name=dict(default=None), 141 | vlan_state=dict(choices=['active', 'suspend'], default='active'), 142 | state=dict(choices=['present', 'absent'], default='present'), 143 | admin_state=dict(choices=['up', 'down'], default='up'), 144 | host=dict(required=True), 145 | username=dict(type='str'), 146 | password=dict(type='str'), 147 | ), 148 | supports_check_mode=True 149 | ) 150 | 151 | auth = Auth(vendor='cisco', model='nexus') 152 | username = module.params['username'] or auth.username 153 | password = module.params['password'] or auth.password 154 | 155 | host = socket.gethostbyname(module.params['host']) 156 | 157 | vlan_id = module.params['vlan_id'] 158 | name = module.params['name'] 159 | vlan_state = module.params['vlan_state'] 160 | admin_state = module.params['admin_state'] 161 | state = module.params['state'] 162 | 163 | device = Device(ip=host, username=username, password=password, 164 | protocol=protocol) 165 | 166 | changed = False 167 | proposed = dict(vlan_id=vlan_id, name=name, vlan_state=vlan_state, 168 | admin_state=admin_state) 169 | 170 | proposed_vlans_list = nxapi_lib.vlan_range_to_list(vlan_id) 171 | proposed_vlans_list.sort() 172 | existing_vlans_list = nxapi_lib.get_list_of_vlans(device) 173 | existing_vlans_list.sort() 174 | 175 | # These are all of the VLANs being proposed that don't already exist 176 | # on the switch 177 | vlans_delta = set(proposed_vlans_list).difference(existing_vlans_list) 178 | 179 | # VLANs that are common between what is being proposed and what is on 180 | # the switch 181 | vlans_common = set(proposed_vlans_list).intersection(existing_vlans_list) 182 | 183 | if state == 'absent' and (vlan_id == '1' or '1' in vlans_common): 184 | module.fail_json(msg="You cannot remove VLAN 1. Doh!!") 185 | 186 | if len(proposed_vlans_list) > 1: 187 | if state == 'present': 188 | my_vlans = list(vlans_delta) 189 | 190 | name_param = proposed.get('name', None) 191 | if name_param and vlans_delta: 192 | module.fail_json(msg="You cannot set the name for multiple " 193 | + "VLANs. Remove the name parameter from " 194 | + "being used.") 195 | elif state == 'absent': 196 | my_vlans = list(vlans_common) 197 | else: 198 | my_vlans = proposed_vlans_list 199 | 200 | # my_vlans holds the VLANs that will be manipulated in some way 201 | 202 | final_existing = {} 203 | final_proposed = {} 204 | final_commands = {} 205 | final_postrun = {} 206 | 207 | for vlan in my_vlans: 208 | 209 | existing = nxapi_lib.get_vlan(device, vlan) 210 | delta = set() 211 | commands = [] 212 | 213 | if state == 'absent': 214 | if existing: 215 | command = nxapi_lib.get_remove_vlan_commands(device, vlan) 216 | commands.append(command) 217 | 218 | elif state == 'present': 219 | if int(vlan) > 1005 and admin_state == 'down': 220 | module.fail_json(msg='You cannot shutdown VLANs > 1005') 221 | proposed = dict(vlan_id=vlan, 222 | vlan_state=vlan_state, 223 | admin_state=admin_state, 224 | name=name) 225 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 226 | if delta: 227 | command = nxapi_lib.get_vlan_config_commands(device, 228 | delta, 229 | vlan) 230 | commands.append(command) 231 | 232 | cmds = '' 233 | if commands: 234 | for each in commands: 235 | cmds = nxapi_lib.cmd_list_to_string(each) 236 | final_commands[vlan] = cmds 237 | 238 | final_existing[vlan] = existing 239 | final_proposed[vlan] = dict(vlan_id=vlan, 240 | vlan_state=vlan_state, 241 | admin_state=admin_state) 242 | 243 | if final_commands: 244 | if module.check_mode: 245 | module.exit_json(changed=True, commands=final_commands) 246 | else: 247 | for vlan, commands in final_commands.iteritems(): 248 | device.config(commands) 249 | changed = True 250 | for vlan in my_vlans: 251 | final_postrun[vlan] = nxapi_lib.get_vlan(device, vlan) 252 | 253 | results = {} 254 | results['proposed'] = proposed 255 | results['existing'] = final_existing 256 | results['new'] = final_postrun 257 | results['state'] = state 258 | results['commands'] = final_commands 259 | results['changed'] = changed 260 | 261 | module.exit_json(**results) 262 | 263 | from ansible.module_utils.basic import * 264 | main() 265 | -------------------------------------------------------------------------------- /library/nxos_vpc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: nxos_vpc 20 | short_description: Manages global VPC configuration 21 | description: 22 | - Manages global VPC configuration 23 | author: Jason Edelman (@jedelman8) 24 | requirements: 25 | - NX-API 1.0 26 | - NX-OS 6.1(2)I3(1) 27 | - pycsco 28 | notes: 29 | - The feature vpc must be enabled before this module can be used 30 | - If not using management vrf, vrf must be globally on the device 31 | before using in the pkl config 32 | - Although source IP isn't required on the command line it is 33 | required when using this module. The PKL VRF must also be configured 34 | prior to using this module. 35 | - While username and password are not required params, they are 36 | if you are not using the .netauth file. .netauth file is recommended 37 | as it will clean up the each task in the playbook by not requiring 38 | the username and password params for every tasks. 39 | - Using the username and password params will override the .netauth file 40 | options: 41 | domain: 42 | description: 43 | - VPC domain 44 | required: true 45 | default: null 46 | choices: [] 47 | aliases: [] 48 | role_priority: 49 | description: 50 | - Role priority for device. Remember lower is better. 51 | required: false 52 | default: null 53 | choices: [] 54 | aliases: [] 55 | system_priority: 56 | description: 57 | - System priority device. Remember they must match between peers. 58 | required: false 59 | default: null 60 | choices: [] 61 | aliases: [] 62 | pkl_src: 63 | description: 64 | - Source IP address used for peer keepalive link 65 | required: false 66 | default: null 67 | choices: [] 68 | aliases: [] 69 | pkl_dest: 70 | description: 71 | - Destination (remote) IP address used for peer keepalive link 72 | required: false 73 | default: null 74 | choices: [] 75 | aliases: [] 76 | pkl_vrf: 77 | description: 78 | - VRF used for peer keepalive link 79 | required: false 80 | default: management 81 | choices: [] 82 | aliases: [] 83 | peer_gw: 84 | description: 85 | - Enables/Disables peer gateway 86 | required: false 87 | default: null 88 | choices: ['true','false'] 89 | aliases: [] 90 | auto_recovery: 91 | description: 92 | - Enables/Disables auto recovery 93 | required: false 94 | default: null 95 | choices: ['true','false'] 96 | aliases: [] 97 | delay_restore: 98 | description: 99 | - manages delay restore command and config value in seconds 100 | required: false 101 | default: null 102 | choices: [] 103 | aliases: [] 104 | state: 105 | description: 106 | - Manages desired state of the resource 107 | required: true 108 | default: present 109 | choices: ['present','absent'] 110 | aliases: [] 111 | host: 112 | description: 113 | - IP Address or hostname (resolvable by Ansible control host) 114 | of the target NX-API enabled switch 115 | required: true 116 | default: null 117 | choices: [] 118 | aliases: [] 119 | username: 120 | description: 121 | - Username used to login to the switch 122 | required: false 123 | default: null 124 | choices: [] 125 | aliases: [] 126 | password: 127 | description: 128 | - Password used to login to the switch 129 | required: false 130 | default: null 131 | choices: [] 132 | aliases: [] 133 | protocol: 134 | description: 135 | - Dictates connection protocol to use for NX-API 136 | required: false 137 | default: http 138 | choices: ['http', 'https'] 139 | aliases: [] 140 | ''' 141 | EXAMPLES = ''' 142 | # ensure vpc domain 100 is configured 143 | - nxos_vpc: domain=100 role_priority=1000 system_priority=2000 pkl_src=192.168.100.1 pkl_dest=192.168.100.2 host={{ inventory_hostname }} 144 | 145 | # ensure peer gateway is enabled for vpc domain 100 146 | - nxos_vpc: domain=100 peer_gw=true host={{ inventory_hostname }} 147 | 148 | # ensure vpc domain does not exist on switch 149 | - nxos_vpc: domain=100 host={{ inventory_hostname }} state=absent 150 | ''' 151 | 152 | try: 153 | import socket 154 | from pycsco.nxos.device import Device 155 | from pycsco.nxos.device import Auth 156 | from pycsco.nxos.utils import nxapi_lib 157 | except ImportError as e: 158 | print '*' * 30 159 | print e 160 | print '*' * 30 161 | 162 | 163 | def main(): 164 | 165 | module = AnsibleModule( 166 | argument_spec=dict( 167 | domain=dict(required=True, type='str'), 168 | role_priority=dict(type='str'), 169 | system_priority=dict(type='str'), 170 | pkl_src=dict(), 171 | pkl_dest=dict(), 172 | pkl_vrf=dict(default='management'), 173 | peer_gw=dict(choices=BOOLEANS, type='bool'), 174 | auto_recovery=dict(choices=BOOLEANS, type='bool'), 175 | delay_restore=dict(type='str'), 176 | state=dict(choices=['absent', 'present'], default='present'), 177 | host=dict(required=True), 178 | username=dict(type='str'), 179 | password=dict(type='str'), 180 | ), 181 | supports_check_mode=True 182 | ) 183 | 184 | auth = Auth(vendor='cisco', model='nexus') 185 | username = module.params['username'] or auth.username 186 | password = module.params['password'] or auth.password 187 | 188 | host = socket.gethostbyname(module.params['host']) 189 | 190 | domain = module.params['domain'] 191 | role_priority = module.params['role_priority'] 192 | system_priority = module.params['system_priority'] 193 | pkl_src = module.params['pkl_src'] 194 | pkl_dest = module.params['pkl_dest'] 195 | pkl_vrf = module.params['pkl_vrf'] 196 | peer_gw = module.params['peer_gw'] 197 | auto_recovery = module.params['auto_recovery'] 198 | delay_restore = module.params['delay_restore'] 199 | state = module.params['state'] 200 | 201 | device = Device(ip=host, username=username, password=password, 202 | protocol=protocol) 203 | 204 | changed = False 205 | 206 | args = dict(domain=domain, role_priority=role_priority, 207 | system_priority=system_priority, pkl_src=pkl_src, 208 | pkl_dest=pkl_dest, pkl_vrf=pkl_vrf, peer_gw=peer_gw, 209 | auto_recovery=auto_recovery, 210 | delay_restore=delay_restore) 211 | 212 | # At this time, requiring pkl_src AND pkl_dest to be set 213 | # VRF is set by default as 'management'- in reality, this is just checking 214 | # on pkl_src and pkl_dest 215 | if not (pkl_src and pkl_dest and pkl_vrf): 216 | # if only the source or dest is set, it'll fail and ask to set the 217 | # other 218 | if pkl_src or pkl_dest: 219 | module.fail_json(msg='source AND dest IP for pkl are required at ' 220 | + 'this time (although source is technically not ' 221 | + ' required by the device.)') 222 | 223 | # These params are removed if only the source and dest are not set 224 | # If that occurs, the VRF is also removed such that nothing changes 225 | # on the peer keepalive link 226 | # Then the only changes will be for rp, sp, peergw, ar, delay_restore, 227 | # etc. 228 | 229 | # module.fail_json(msg='When you are using the pkl_vrf param, you should' 230 | # + ' really also set the source and dest IPs of the ' 231 | # + 'peer keepalive link)') 232 | args.pop('pkl_src') 233 | args.pop('pkl_dest') 234 | args.pop('pkl_vrf') 235 | 236 | if not nxapi_lib.feature_enabled(device, 'vpc'): 237 | module.fail_json(msg='VPC feature needs to be enabled first') 238 | 239 | if pkl_vrf: 240 | if pkl_vrf.lower() not in nxapi_lib.get_vrf_list(device): 241 | module.fail_json(msg='The VRF you are trying to use for the peer ' 242 | + 'keepalive link is not on device yet. Add it ' 243 | + 'first, please.') 244 | 245 | proposed = {} 246 | for param, value in args.iteritems(): 247 | if value is not None: 248 | proposed[param] = value 249 | 250 | existing = nxapi_lib.get_vpc(device) 251 | 252 | commands = [] 253 | if state == 'present': 254 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 255 | command = nxapi_lib.get_commands_to_config_vpc(delta, domain, existing) 256 | commands.append(command) 257 | elif state == 'absent': 258 | if existing: 259 | if domain != existing['domain']: 260 | module.fail_json(msg="You are trying to remove a domain that " 261 | + "does not exist on the device") 262 | else: 263 | command = nxapi_lib.get_commands_to_remove_vpc(domain) 264 | commands.append(command) 265 | cmds = '' 266 | if commands: 267 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 268 | for each in commands if each) 269 | 270 | if cmds: 271 | if module.check_mode: 272 | module.exit_json(changed=True, commands=cmds) 273 | else: 274 | changed = True 275 | device.config(cmds) 276 | 277 | results = {} 278 | results['proposed'] = proposed 279 | results['existing'] = existing 280 | results['new'] = nxapi_lib.get_vpc(device) 281 | results['state'] = state 282 | results['commands'] = cmds 283 | results['changed'] = changed 284 | 285 | module.exit_json(**results) 286 | 287 | from ansible.module_utils.basic import * 288 | main() 289 | -------------------------------------------------------------------------------- /library/nxos_vpc_interface: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: nxos_vpc_interface 20 | short_description: Manages interface VPC configuration 21 | description: 22 | - Manages interface VPC configuration 23 | author: Cisco Systems Inc. 24 | requirements: 25 | - NX-API 1.0 26 | - NX-OS 6.1(2)I3(1) 27 | - pycsco 28 | notes: 29 | - Either vpc or peer_link param is required, but not both. 30 | - State=absent removes whatever VPC config is on a port-channel 31 | if one exists. 32 | - Re-assigning a vpc or peerlink from one portchannel to another is not 33 | supported. The module will force the user to unconfigure an existing 34 | vpc/pl before configuring the same value on a new portchannel 35 | - While username and password are not required params, they are 36 | if you are not using the .netauth file. .netauth file is recommended 37 | as it will clean up the each task in the playbook by not requiring 38 | the username and password params for every tasks. 39 | - Using the username and password params will override the .netauth file 40 | options: 41 | portchannel: 42 | description: 43 | - group number of the portchannel that will be configured 44 | required: true 45 | default: null 46 | choices: [] 47 | aliases: [] 48 | vpc: 49 | description: 50 | - vpc group/id that will be configured on associated portchannel 51 | required: false 52 | default: null 53 | choices: [] 54 | aliases: [] 55 | peer_link: 56 | description: 57 | - Set to true/false for peer link config on assoicated portchannel 58 | required: false 59 | default: null 60 | choices: [] 61 | aliases: [] 62 | state: 63 | description: 64 | - Manages desired state of the resource 65 | required: true 66 | default: present 67 | choices: ['present','absent'] 68 | aliases: [] 69 | host: 70 | description: 71 | - IP Address or hostname (resolvable by Ansible control host) 72 | of the target NX-API enabled switch 73 | required: true 74 | default: null 75 | choices: [] 76 | aliases: [] 77 | username: 78 | description: 79 | - Username used to login to the switch 80 | required: false 81 | default: null 82 | choices: [] 83 | aliases: [] 84 | password: 85 | description: 86 | - Password used to login to the switch 87 | required: false 88 | default: null 89 | choices: [] 90 | aliases: [] 91 | protocol: 92 | description: 93 | - Dictates connection protocol to use for NX-API 94 | required: false 95 | default: http 96 | choices: ['http', 'https'] 97 | aliases: [] 98 | ''' 99 | EXAMPLES = ''' 100 | # config portchannel10 to be the peerlink 101 | #- nxos_vpc_interface: portchannel=10 peer_link=true host={{ inventory_hostname }} 102 | 103 | # config portchannel20 to be vpc20 104 | #- nxos_vpc_interface: portchannel=20 vpc=20 host={{ inventory_hostname }} 105 | 106 | # remove whatever VPC config is on portchannel if any exists (vpc xx or vpc peer-link) 107 | - nxos_vpc_interface: portchannel=80 host={{ inventory_hostname }} state=absent 108 | ''' 109 | 110 | try: 111 | import socket 112 | from pycsco.nxos.device import Device 113 | from pycsco.nxos.device import Auth 114 | from pycsco.nxos.utils import nxapi_lib 115 | except ImportError as e: 116 | print '*' * 30 117 | print e 118 | print '*' * 30 119 | 120 | 121 | def main(): 122 | 123 | module = AnsibleModule( 124 | argument_spec=dict( 125 | portchannel=dict(required=True, type='str'), 126 | vpc=dict(type='str'), 127 | peer_link=dict(choices=BOOLEANS, type='bool'), 128 | state=dict(choices=['absent', 'present'], default='present'), 129 | host=dict(required=True), 130 | username=dict(type='str'), 131 | password=dict(type='str'), 132 | ), 133 | supports_check_mode=True 134 | ) 135 | 136 | auth = Auth(vendor='cisco', model='nexus') 137 | username = module.params['username'] or auth.username 138 | password = module.params['password'] or auth.password 139 | 140 | host = socket.gethostbyname(module.params['host']) 141 | 142 | portchannel = module.params['portchannel'] 143 | vpc = module.params['vpc'] 144 | peer_link = module.params['peer_link'] 145 | state = module.params['state'] 146 | 147 | device = Device(ip=host, username=username, password=password, 148 | protocol=protocol) 149 | device.open() 150 | 151 | changed = False 152 | # args = dict(portchannel=portchannel, vpc=vpc, peer_link=peer_link) 153 | 154 | if not nxapi_lib.feature_enabled(device, 'vpc'): 155 | module.fail_json(msg='VPC feature needs to be enabled first') 156 | config_value = None 157 | if state == 'present': 158 | if vpc and (peer_link or peer_link is False): 159 | module.fail_json(msg="Only one of the params ['vpc', 'peer_link'] " 160 | + "should be used at a given time") 161 | 162 | if vpc or (peer_link or peer_link is False): 163 | if portchannel not in nxapi_lib.get_portchannel_list(device): 164 | module.fail_json(msg="The portchannel you are trying to make a" 165 | + " VPC or PL is not created yet. " 166 | + "Create it first!") 167 | 168 | if not (vpc or (peer_link or peer_link is False)): 169 | module.fail_json(msg="At least one of the params" 170 | + "['vpc', 'peer_link'] is required") 171 | active_peer_link = None 172 | if vpc: 173 | mapping = \ 174 | nxapi_lib.get_existing_portchannel_to_vpc_mappings(device) 175 | if vpc in mapping.keys() and portchannel != mapping[vpc][-2:]: 176 | module.fail_json(msg="This vpc is already configured on " 177 | + "another portchannel. Remove it first " 178 | + "before trying to assign it here. ", 179 | existing_portchannel=mapping[vpc]) 180 | 181 | for vpcid, existing_pc in mapping.iteritems(): 182 | if portchannel == existing_pc[-2:] and vpcid != vpc: 183 | module.exit_json(msg="This portchannel already has another" 184 | + " VPC configured. Remove it first " 185 | + "before assigning this one", 186 | existing_vpc=vpcid) 187 | if nxapi_lib.peer_link_exists(device): 188 | active_peer_link = nxapi_lib.get_active_vpc_peer_link(device) 189 | if active_peer_link[-2:] == portchannel: 190 | module.fail_json(msg="That port channel is the current" 191 | + " PEER LINK. Remove it if you want it" 192 | + " to be a VPC") 193 | config_value = vpc 194 | 195 | elif (peer_link or peer_link is False): 196 | if nxapi_lib.peer_link_exists(device): 197 | active_peer_link = \ 198 | nxapi_lib.get_active_vpc_peer_link(device)[-2:] 199 | if active_peer_link != portchannel: 200 | if peer_link: 201 | module.fail_json(msg="A peer link already exists on" 202 | + " the device. Remove it first", 203 | current_peer_link='Po' 204 | + active_peer_link) 205 | config_value = 'peer-link' 206 | 207 | proposed = dict(portchannel=portchannel, config_value=config_value) 208 | existing = nxapi_lib.get_portchannel_vpc_config(device, portchannel) 209 | commands = [] 210 | 211 | if state == 'present': 212 | if peer_link is False and portchannel != active_peer_link: 213 | # this is offering the user the ability to do portchannel=XX and 214 | # peer_link=false just 215 | # to ensure that the portchannel is not a peer-link 216 | pass 217 | elif not existing: 218 | command = nxapi_lib.get_commands_to_config_vpc_interface( 219 | portchannel, 220 | proposed['config_value'] 221 | ) 222 | commands.append(command) 223 | elif ( 224 | (vpc and (existing != config_value)) or 225 | (peer_link is False and config_value == 'peer-link') 226 | ): 227 | command = nxapi_lib.get_commands_to_remove_vpc_interface( 228 | portchannel, 229 | proposed['config_value'] 230 | ) 231 | commands.append(command) 232 | 233 | elif state == 'absent': 234 | if existing: 235 | command = ['no vpc ' + existing] 236 | commands.append(command) 237 | commands.insert(0, ['interface port-channel' + portchannel]) 238 | cmds = '' 239 | if commands: 240 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 241 | for each in commands if each) 242 | 243 | if cmds: 244 | if module.check_mode: 245 | module.exit_json(changed=True, commands=cmds) 246 | else: 247 | changed = True 248 | device.config(cmds) 249 | 250 | results = {} 251 | results['proposed'] = proposed 252 | results['existing'] = existing 253 | results['new'] = nxapi_lib.get_portchannel_vpc_config(device, portchannel) 254 | results['state'] = state 255 | results['commands'] = cmds 256 | results['changed'] = changed 257 | 258 | module.exit_json(**results) 259 | 260 | from ansible.module_utils.basic import * 261 | main() 262 | -------------------------------------------------------------------------------- /library/nxos_vrf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: nxos_vrf 20 | short_description: Manages global VRF configuration 21 | description: 22 | - Manages global VRF configuration 23 | author: Jason Edelman (@jedelman8) 24 | requirements: 25 | - NX-API 1.0 26 | - NX-OS 6.1(2)I3(1) 27 | - pycsco 28 | notes: 29 | - While username and password are not required params, they are 30 | if you are not using the .netauth file. .netauth file is recommended 31 | as it will clean up the each task in the playbook by not requiring 32 | the username and password params for every tasks. 33 | - Using the username and password params will override the .netauth file 34 | options: 35 | vrf: 36 | description: 37 | - Name of VRF to be managed 38 | required: true 39 | default: null 40 | choices: [] 41 | aliases: [] 42 | admin_state: 43 | description: 44 | - Administrative state of the VRF 45 | required: false 46 | default: up 47 | choices: ['up','down'] 48 | aliases: [] 49 | state: 50 | description: 51 | - Manages desired state of the resource 52 | required: true 53 | default: present 54 | choices: ['present','absent'] 55 | aliases: [] 56 | description: 57 | description: 58 | - Description of the VRF 59 | required: false 60 | default: null 61 | choices: [] 62 | aliases: [] 63 | host: 64 | description: 65 | - IP Address or hostname (resolvable by Ansible control host) 66 | of the target NX-API enabled switch 67 | required: true 68 | default: null 69 | choices: [] 70 | aliases: [] 71 | username: 72 | description: 73 | - Username used to login to the switch 74 | required: false 75 | default: null 76 | choices: [] 77 | aliases: [] 78 | password: 79 | description: 80 | - Password used to login to the switch 81 | required: false 82 | default: null 83 | choices: [] 84 | aliases: [] 85 | protocol: 86 | description: 87 | - Dictates connection protocol to use for NX-API 88 | required: false 89 | default: http 90 | choices: ['http', 'https'] 91 | aliases: [] 92 | ''' 93 | EXAMPLES = ''' 94 | # ensure yankees VRF exists on switch 95 | - nxos_vrf: vrf=yankees host={{ inventory_hostname }} 96 | 97 | # ensure yankees VRF does not exist on switch 98 | - nxos_vrf: vrf=yankees host={{ inventory_hostname }} state=absent 99 | ''' 100 | 101 | try: 102 | import socket 103 | from pycsco.nxos.device import Device 104 | from pycsco.nxos.device import Auth 105 | from pycsco.nxos.utils import nxapi_lib 106 | except ImportError as e: 107 | print '*' * 30 108 | print e 109 | print '*' * 30 110 | 111 | 112 | def main(): 113 | 114 | module = AnsibleModule( 115 | argument_spec=dict( 116 | vrf=dict(required=True), 117 | admin_state=dict(default='up', choices=['up', 'down']), 118 | state=dict(default='present', choices=['present', 'absent']), 119 | description=dict(default=None), 120 | host=dict(required=True), 121 | username=dict(type='str'), 122 | password=dict(type='str'), 123 | ), 124 | supports_check_mode=True 125 | ) 126 | 127 | auth = Auth(vendor='cisco', model='nexus') 128 | username = module.params['username'] or auth.username 129 | password = module.params['password'] or auth.password 130 | 131 | host = socket.gethostbyname(module.params['host']) 132 | 133 | vrf = module.params['vrf'] 134 | admin_state = module.params['admin_state'] 135 | description = module.params['description'] 136 | state = module.params['state'] 137 | 138 | device = Device(ip=host, username=username, password=password, 139 | protocol=protocol) 140 | 141 | changed = False 142 | 143 | args = dict(vrf=vrf, description=description, admin_state=admin_state) 144 | 145 | if vrf.lower() == 'default': 146 | module.fail_json(msg='cannot use default as name of a VRF') 147 | 148 | proposed = {} 149 | for param, value in args.iteritems(): 150 | if value: 151 | proposed[param] = value 152 | 153 | existing = nxapi_lib.get_vrf(device, vrf) 154 | 155 | # this code block is making sure the user input vrf 156 | # is in the right case before moving forward 157 | if existing: 158 | existing_vrf = existing.get('vrf', None) 159 | if existing_vrf: 160 | if existing_vrf.lower() == vrf.lower(): 161 | proposed['vrf'] == existing['vrf'] 162 | 163 | commands = [] 164 | if state == 'absent': 165 | if existing: 166 | command = nxapi_lib.get_commands_to_remove_vrf(vrf) 167 | commands.append(command) 168 | 169 | elif state == 'present': 170 | delta = set(proposed.iteritems()).difference(existing.iteritems()) 171 | if not existing: 172 | command = nxapi_lib.get_commands_to_config_vrf(delta, vrf) 173 | commands.append(command) 174 | elif delta: 175 | check_delta = dict(delta) 176 | # vrf should NOT be in this set/dict if it already exists 177 | # however, due to case sensitivity, this is being used as a check 178 | # can't just change case because it's case is needed within 179 | # methods in 180 | # the lib 181 | if 'vrf' not in check_delta.keys(): 182 | command = nxapi_lib.get_commands_to_config_vrf(delta, vrf) 183 | commands.append(command) 184 | cmds = '' 185 | if commands: 186 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 187 | for each in commands if each) 188 | 189 | if cmds: 190 | if module.check_mode: 191 | module.exit_json(changed=True, commands=cmds) 192 | else: 193 | changed = True 194 | device.config(cmds) 195 | 196 | results = {} 197 | results['proposed'] = proposed 198 | results['existing'] = existing 199 | results['new'] = nxapi_lib.get_vrf(device, vrf) 200 | results['state'] = state 201 | results['commands'] = cmds 202 | results['changed'] = changed 203 | 204 | module.exit_json(**results) 205 | 206 | from ansible.module_utils.basic import * 207 | main() 208 | -------------------------------------------------------------------------------- /library/nxos_vrf_interface: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015 Jason Edelman 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: nxos_vrf_interface 20 | short_description: Manages interface specific VRF configuration 21 | description: 22 | - Manages interface specific VRF configuration 23 | author: Jason Edelman (@jedelman8) 24 | requirements: 25 | - NX-API 1.0 26 | - NX-OS 6.1(2)I3(1) 27 | - pycsco 28 | notes: 29 | - VRF needs to be added globally with nxos_vrf before 30 | adding a VRF to an interface 31 | - Remove a VRF from an interface will still remove 32 | all L3 attributes just as it does from CLI 33 | - VRF is not read from an interface until IP address is 34 | configured on that interface 35 | - While username and password are not required params, they are 36 | if you are not using the .netauth file. .netauth file is recommended 37 | as it will clean up the each task in the playbook by not requiring 38 | the username and password params for every tasks. 39 | - Using the username and password params will override the .netauth file 40 | options: 41 | vrf: 42 | description: 43 | - Name of VRF to be managed 44 | required: true 45 | default: null 46 | choices: [] 47 | aliases: [] 48 | interface: 49 | description: 50 | - Full name of interface to be managed, i.e. Ethernet1/1 51 | required: true 52 | default: null 53 | choices: [] 54 | aliases: [] 55 | state: 56 | description: 57 | - Manages desired state of the resource 58 | required: true 59 | default: present 60 | choices: ['present','absent'] 61 | aliases: [] 62 | host: 63 | description: 64 | - IP Address or hostname (resolvable by Ansible control host) 65 | of the target NX-API enabled switch 66 | required: true 67 | default: null 68 | choices: [] 69 | aliases: [] 70 | username: 71 | description: 72 | - Username used to login to the switch 73 | required: false 74 | default: null 75 | choices: [] 76 | aliases: [] 77 | password: 78 | description: 79 | - Password used to login to the switch 80 | required: false 81 | default: null 82 | choices: [] 83 | aliases: [] 84 | protocol: 85 | description: 86 | - Dictates connection protocol to use for NX-API 87 | required: false 88 | default: http 89 | choices: ['http', 'https'] 90 | aliases: [] 91 | ''' 92 | EXAMPLES = ''' 93 | # ensure vrf yankees exists on Eth1/1 94 | - nxos_vrf_interface: vrf=yankees interface=Ethernet1/1 host={{ inventory_hostname }} state=present 95 | 96 | # ensure yankees VRF does not exist on Eth1/1 97 | - nxos_vrf_interface: vrf=yankees interface=Ethernet1/1 host={{ inventory_hostname }} state=absent 98 | ''' 99 | 100 | try: 101 | import socket 102 | from pycsco.nxos.device import Device 103 | from pycsco.nxos.device import Auth 104 | from pycsco.nxos.utils import nxapi_lib 105 | except ImportError as e: 106 | print '*' * 30 107 | print e 108 | print '*' * 30 109 | 110 | 111 | def main(): 112 | 113 | module = AnsibleModule( 114 | argument_spec=dict( 115 | vrf=dict(required=True), 116 | interface=dict(type='str'), 117 | state=dict(default='present', choices=['present', 'absent']), 118 | host=dict(required=True), 119 | username=dict(type='str'), 120 | password=dict(type='str'), 121 | ), 122 | supports_check_mode=True 123 | ) 124 | 125 | auth = Auth(vendor='cisco', model='nexus') 126 | username = module.params['username'] or auth.username 127 | password = module.params['password'] or auth.password 128 | 129 | host = socket.gethostbyname(module.params['host']) 130 | 131 | vrf = module.params['vrf'].lower() 132 | interface = module.params['interface'].lower() 133 | state = module.params['state'] 134 | 135 | device = Device(ip=host, username=username, password=password, 136 | protocol=protocol) 137 | changed = False 138 | 139 | intf_type = nxapi_lib.get_interface_type(interface) 140 | 141 | if intf_type == 'ethernet': 142 | mode = nxapi_lib.get_interface_mode(device, interface) 143 | if mode == 'layer2': 144 | module.fail_json(msg='Ensure interface is a Layer 3 port before' 145 | + 'configuring a VRF on an interface. You can ' 146 | + ' use nxapi_interface') 147 | 148 | if intf_type != 'ethernet' \ 149 | and nxapi_lib.is_default(device, interface) == 'DNE': 150 | module.fail_json(msg='interface does not exist on switch. Verify ' 151 | + 'switch platform or create it first with ' 152 | + "nxapi_interface if it's a logical interface") 153 | 154 | proposed = dict(interface=interface, vrf=vrf) 155 | existing_ipv4 = nxapi_lib.get_ipv4_interface(device, interface) 156 | existing = {} 157 | 158 | if existing_ipv4: 159 | existing['vrf'] = existing_ipv4['vrf'] 160 | existing['interface'] = existing_ipv4['interface'].lower() 161 | 162 | current_vrfs = nxapi_lib.get_vrf_list(device) 163 | 164 | if vrf not in current_vrfs: 165 | module.fail_json(msg='Ensure the VRF you are trying to config/remove ' 166 | + 'to an interface is created globally on the device' 167 | + ' first.') 168 | ''' 169 | if vrf != existing['vrf'] and state == 'absent': 170 | module.fail_json(msg='The VRF you are trying to 171 | remove from the interface ' \ 172 | + 'does not exist on that interface.', 173 | interface=interface,proposed_vrf=vrf, \ 174 | vrf_on_interface=existing['vrf']) 175 | ''' 176 | 177 | commands = [] 178 | if state == 'absent': 179 | if existing and vrf == existing['vrf']: 180 | command = ['no vrf member ' + vrf] 181 | commands.append(command) 182 | 183 | elif state == 'present': 184 | if existing['vrf'] != vrf: 185 | command = ['vrf member ' + vrf] 186 | commands.append(command) 187 | cmds = '' 188 | if commands: 189 | commands.insert(0, ['interface ' + interface]) 190 | cmds = ' '.join(nxapi_lib.cmd_list_to_string(each) 191 | for each in commands if each) 192 | 193 | if cmds: 194 | if module.check_mode: 195 | module.exit_json(changed=True, commands=cmds) 196 | else: 197 | changed = True 198 | device.config(cmds) 199 | 200 | postrun_ipv4 = nxapi_lib.get_ipv4_interface(device, interface) 201 | postrun = {} 202 | if postrun_ipv4: 203 | postrun['vrf'] = postrun_ipv4['vrf'] 204 | postrun['interface'] = postrun_ipv4['interface'].lower() 205 | 206 | results = {} 207 | results['proposed'] = proposed 208 | results['existing'] = existing 209 | results['new'] = postrun 210 | results['state'] = state 211 | results['commands'] = cmds 212 | results['changed'] = changed 213 | 214 | module.exit_json(**results) 215 | 216 | from ansible.module_utils.basic import * 217 | main() 218 | -------------------------------------------------------------------------------- /templates/neighbors.j2: -------------------------------------------------------------------------------- 1 | 2 | {{ my_neighbors.resource | to_nice_json }} -------------------------------------------------------------------------------- /templates/routers.j2: -------------------------------------------------------------------------------- 1 | hostname {{ inventory_hostname }} 2 | 3 | interface mgmt0 4 | ip address {{ mgmt_ip }} 5 | 6 | ntp server {{ ntp_server }} 7 | snmp-server {{ snmp_server }} 8 | 9 | username cisco password cisco 10 | 11 | --------------------------------------------------------------------------------