CiscoDevNet / ansible-pyats

Other
46 stars 12 forks source link

ansible-pyats

ansible-genie is a implementation of the pyATS network testing framework in an Ansible role. It contains modules, filters, and tasks:

Installation

First, install the Python dependencies:

$ pip install pyats genie
<snip>
Installing collected packages: pyats, genie
Successfully installed genie-19.9 pyats-19.9.2

pyATS and Genie require Python >=3.4.

Manual

For manual installation, you can just clone the repository into your ANSIBLE_ROLES_PATH:

$ git clone https://github.com/CiscoDevNet/ansible-pyats "${ANSIBLE_ROLES_PATH:-roles}/ansible-pyats"
Cloning into 'roles/ansible-pyats'...
remote: Enumerating objects: 83, done.
remote: Counting objects: 100% (83/83), done.
remote: Compressing objects: 100% (56/56), done.
remote: Total 83 (delta 28), reused 56 (delta 12), pack-reused 0
Unpacking objects: 100% (83/83), done.

Ansible Galaxy

If you are using Ansible Galaxy, you can use this role by adding the following to your requirements.yml:

- src: https://github.com/CiscoDevNet/ansible-pyats
  scm: git
  name: ansible-pyats

Next, install your Galaxy dependencies:

$ ansible-galaxy install -r requirements.yml -p "${ANSIBLE_ROLES_PATH:-roles}"

Modules

Filters

Example Playbooks

Run a command and retrieve the structured output

- hosts: router
  connection: network_cli
  gather_facts: no
  roles:
    - ansible-pyats
  tasks:
    - pyats_parse_command:
        command: show ip route bgp
      register: output

    - debug:
        var: output.structured

Snapshot the output of a command to a file

- hosts: router
  connection: network_cli
  gather_facts: no
  roles:
    - ansible-pyats
  tasks:
    - include_role:
        name: ansible-pyats
        tasks_from: snapshot_command
      vars:
        command: show ip route
        file: "{{ inventory_hostname }}_routes.json"

Role Variables

Compare the output of a command with a previous snapshot

- hosts: router
  connection: network_cli
  gather_facts: no
  roles:
    - ansible-pyats
  tasks:
    - include_role:
        name: ansible-pyats
        tasks_from: compare_command
      vars:
        command: show ip route
        file: "{{ inventory_hostname }}_routes.json"

Role Variables

Using the pyats_parser filter directly

- hosts: router
  connection: network_cli
  gather_facts: no
  roles:
    - ansible-pyats
  tasks:
    - name: Run command
      cli_command:
        command: show ip route
      register: cli_output

    - name: Parsing output
      set_fact:
        parsed_output: "{{ cli_output.stdout | pyats_parser('show ip route', 'iosxe') }}"

Using the pyats_diff filter directly

- name: Diff current and snapshot
  set_fact:
    diff_output: "{{ current_output | pyats_diff(previous_output) }}"

Change ACL configuration and compare before and after configs using genie_config_diff

---

- hosts: cisco
  gather_facts: no
  connection: network_cli

  tasks:
    - name: collect config (before)
      ios_command:
        commands:
          - show run
      register: result_before

    - name: load new acl into device
      ios_config:
        lines:
          - permit ip host 192.168.114.1 any
          - permit ip host 192.168.114.2 any
          - permit ip host 192.168.114.3 any
        parents: ip access-list extended test
        save_when: modified

    - name: collect config (after)
      ios_command:
        commands:
          - show run
      register: result_after

    - name: debug
      debug:
        msg: "{{ result_before.stdout[0] | genie_config_diff(result_after.stdout[0], mode='add', exclude=exclude_list) }}"

  vars:
    exclude_list:
      - (^Using.*)
      - (Building.*)
      - (Current.*)
      - (crypto pki certificate chain.*)

The argument mode can be add (displays added commands in result_after), remove (displays removed commands in result_after), or modified (displays modified commands). If mode argument is not specified, added, removed, and modified commands are displayed.
The argument exclude means command lists which is excluded when comparing before and after configs. In the playbook example above, variable excluded_list, which is defined as the play variable, is used.

Other examples

        msg: "{{ result_before.stdout[0] | genie_config_diff(result_after.stdout[0]) }}"
        msg: "{{ result_before.stdout[0] | genie_config_diff(result_after.stdout[0], mode='remove') }}"
        msg: "{{ result_before.stdout[0] | genie_config_diff(result_after.stdout[0], exclude=exclude_list) }}"

The result of example playbook

PLAY [cisco] **********************************************************************************

TASK [collect config (before)] ****************************************************************
ok: [test3]

TASK [load new acl into device] ***************************************************************
ok: [test3]

TASK [collect config (after)] *****************************************************************
ok: [test3]

TASK [debug] **********************************************************************************
ok: [test3] => {
    "msg": [
        "ip access-list extended test:",
        "+ permit ip host 192.168.114.1 any: ",
        "+ permit ip host 192.168.114.2 any: ",
        "+ permit ip host 192.168.114.3 any: "
    ]
}

PLAY RECAP ************************************************************************************
test3                      : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Compare show commands using genie_parser_diff

This filter can compare the output of show commands parsed by Genie parser. The arguments mode and exclude also can be used.

    - name: debug
      debug:
        msg: "{{ sh_int_parsed_before | genie_parser_diff(sh_int_parsed_after, mode='modified', exclude=exclude_list) }}"

  vars:
    exclude_list:
      - (.*in_octets.*)
      - (.*in_pkts.*)
      - (.*out_octets.*)
      - (.*out_pkts.*)

License

Cisco Sample License