├── roles ├── ansible-network.network-engine │ ├── lookup_plugins │ │ ├── __init__.py │ │ ├── config_template.py │ │ ├── json_template.py │ │ └── netcfg_diff.py │ ├── lib │ │ └── network_engine │ │ │ ├── __init__.py │ │ │ ├── plugins │ │ │ ├── parser │ │ │ │ ├── __init__.py │ │ │ │ └── pattern_match.py │ │ │ ├── template │ │ │ │ ├── normal.py │ │ │ │ ├── __init__.py │ │ │ │ └── json_template.py │ │ │ └── __init__.py │ │ │ └── utils.py │ ├── requirements.txt │ ├── test-requirements.txt │ ├── vars │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── main.yml │ │ └── cli.yaml │ ├── changelogs │ │ ├── fragments │ │ │ ├── v252-tasks.yaml │ │ │ ├── v267-new-modules.yaml │ │ │ ├── v270-new-modules.yaml │ │ │ ├── v262-filter-plugins.yaml │ │ │ ├── v266-removed-features.yaml │ │ │ ├── v262-modules.yaml │ │ │ ├── v262-lookup-plugins.yaml │ │ │ ├── v251-docs.yaml │ │ │ ├── v264-removed-features.yaml │ │ │ ├── v260-initial-release.yaml │ │ │ ├── v262-bugfixes.yaml │ │ │ ├── v264-bugfixes.yaml │ │ │ ├── v264-docs.yaml │ │ │ ├── v261-bugfixes.yaml │ │ │ ├── v263-bugfixes.yaml │ │ │ ├── v265-bugfixes.yaml │ │ │ ├── v263-minorchanges.yaml │ │ │ ├── v266-minor-changes.yaml │ │ │ ├── v252-bugfixes.yaml │ │ │ ├── v261-docs.yaml │ │ │ ├── v252-lookup-plugins.yaml │ │ │ ├── v252-filter-plugins.yaml │ │ │ ├── v253-minorchanges.yaml │ │ │ ├── v253-removed-features.yaml │ │ │ ├── v254-minorchanges.yaml │ │ │ ├── v252-minorchanges.yaml │ │ │ ├── v254-bugfixes.yaml │ │ │ ├── v251-bugfixes.yaml │ │ │ ├── v251-terminology-changes.yaml │ │ │ └── v0-initial-release.yaml │ │ └── config.yaml │ ├── tests │ │ ├── config_template │ │ │ ├── config_template │ │ │ │ ├── templates │ │ │ │ │ ├── pass.j2 │ │ │ │ │ └── fail.j2 │ │ │ │ └── tasks │ │ │ │ │ ├── main.yaml │ │ │ │ │ └── config_template.yaml │ │ │ └── test.yml │ │ ├── json_template │ │ │ ├── json_template │ │ │ │ ├── defaults │ │ │ │ │ └── main.yaml │ │ │ │ ├── tasks │ │ │ │ │ ├── main.yaml │ │ │ │ │ └── json_lookup.yaml │ │ │ │ └── templates │ │ │ │ │ └── config.json │ │ │ └── test.yml │ │ ├── to_lines │ │ │ ├── test.yml │ │ │ └── to_lines │ │ │ │ └── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── to_lines.yaml │ │ ├── netcfg_diff │ │ │ ├── test.yml │ │ │ └── netcfg_diff │ │ │ │ ├── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── ios.yaml │ │ │ │ ├── defaults │ │ │ │ └── main.yaml │ │ │ │ └── files │ │ │ │ └── ios │ │ │ │ ├── have.txt │ │ │ │ └── want.txt │ │ ├── vlan_expand │ │ │ ├── test.yml │ │ │ └── vlan_expand │ │ │ │ └── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── vlan_expand.yaml │ │ ├── command_parser │ │ │ ├── test.yml │ │ │ └── command_parser │ │ │ │ ├── defaults │ │ │ │ └── main.yaml │ │ │ │ ├── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── ios.yaml │ │ │ │ ├── parser_templates │ │ │ │ └── ios │ │ │ │ │ ├── show_version.yaml │ │ │ │ │ ├── show_version_expand.yaml │ │ │ │ │ ├── show_interfaces.yaml │ │ │ │ │ └── show_interfaces_expand.yaml │ │ │ │ └── output │ │ │ │ └── ios │ │ │ │ ├── show_version.txt │ │ │ │ └── show_interfaces.txt │ │ ├── interface_range │ │ │ ├── test.yml │ │ │ └── interface_range │ │ │ │ └── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── interface_range.yaml │ │ ├── interface_split │ │ │ ├── test.yml │ │ │ └── interface_split │ │ │ │ └── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── interface_split.yaml │ │ ├── textfsm_parser │ │ │ ├── test.yml │ │ │ └── textfsm_parser │ │ │ │ ├── defaults │ │ │ │ └── main.yaml │ │ │ │ ├── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── ios.yaml │ │ │ │ ├── parser_templates │ │ │ │ └── ios │ │ │ │ │ ├── show_version │ │ │ │ │ └── show_interfaces │ │ │ │ └── output │ │ │ │ └── ios │ │ │ │ ├── show_version.txt │ │ │ │ └── show_interfaces.txt │ │ ├── vlan_compress │ │ │ ├── test.yml │ │ │ └── vlan_compress │ │ │ │ └── tasks │ │ │ │ ├── main.yaml │ │ │ │ └── vlan_compress.yaml │ │ ├── validate_role_spec │ │ │ ├── test.yml │ │ │ └── validate_role_spec │ │ │ │ ├── tasks │ │ │ │ ├── validate_role_spec.yaml │ │ │ │ └── main.yaml │ │ │ │ └── meta │ │ │ │ └── test.yaml │ │ └── test.yml │ ├── meta │ │ ├── .galaxy_install_info │ │ └── main.yml │ ├── .zuul.yaml │ ├── bindep.txt │ ├── defaults │ │ └── main.yml │ ├── includes │ │ └── init.yaml │ ├── tox.ini │ ├── .github │ │ └── ISSUE_TEMPLATE.md │ ├── docs │ │ ├── tests │ │ │ └── test_guide.md │ │ ├── plugins │ │ │ └── filter_plugins.md │ │ ├── user_guide │ │ │ ├── textfsm_parser.md │ │ │ ├── README.md │ │ │ └── command_parser.md │ │ ├── tasks │ │ │ └── cli.md │ │ └── directives │ │ │ ├── parser_directives.md │ │ │ └── template_directives.md │ ├── ANNOUNCE.md │ ├── library │ │ ├── net_facts.py │ │ ├── command_parser.py │ │ └── textfsm_parser.py │ ├── README.md │ ├── action_plugins │ │ ├── textfsm_parser.py │ │ ├── validate_role_spec.py │ │ └── cli.py │ ├── CONTRIBUTING.md │ ├── filter_plugins │ │ └── network_engine.py │ └── CHANGELOG.rst ├── 4500_install_image │ ├── vars │ │ └── main.yml │ ├── parsers │ │ ├── 4500_md5_parser.yml │ │ ├── 4500_dir_parser.yml │ │ └── 4500_show_version_parser.yml │ ├── README.md │ ├── meta │ │ └── main.yml │ └── tasks │ │ └── main.yml └── 4500_copy_image │ ├── vars │ └── main.yml │ ├── parsers │ ├── 4500_md5_parser.yml │ ├── 4500_dir_parser.yml │ └── 4500_show_version_parser.yml │ ├── tasks │ └── main.yml │ ├── README.md │ └── meta │ └── main.yml ├── readme_pics ├── 4500-X.jpg ├── copy_flow_chart.jpg └── install_flow_chart.jpg ├── 4500_roles.yml ├── parsers ├── 4500_md5_parser.yml ├── 3560_dir_parser.yml ├── 3560_show_version_parser.yml ├── 4500_dir_parser.yml └── 4500_show_version_parser.yml ├── .github └── workflows │ └── ansible-lint.yml └── README.md /roles/ansible-network.network-engine/lookup_plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/requirements.txt: -------------------------------------------------------------------------------- 1 | ansible 2 | textfsm 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/test-requirements.txt: -------------------------------------------------------------------------------- 1 | ara 2 | flake8 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/plugins/parser/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network-engine 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network-engine -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network-engine 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v252-tasks.yaml: -------------------------------------------------------------------------------- 1 | new_tasks: 2 | - New task ``cli`` 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/config_template/config_template/templates/pass.j2: -------------------------------------------------------------------------------- 1 | test {{ test }} 2 | -------------------------------------------------------------------------------- /readme_pics/4500-X.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colin-mccarthy/ansible_cisco_ios_upgrade/HEAD/readme_pics/4500-X.jpg -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v267-new-modules.yaml: -------------------------------------------------------------------------------- 1 | new_modules: 2 | - Add ``net_facts`` module. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v270-new-modules.yaml: -------------------------------------------------------------------------------- 1 | new_modules: 2 | - Add ``net_facts`` module. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/meta/.galaxy_install_info: -------------------------------------------------------------------------------- 1 | {install_date: 'Wed Oct 17 02:11:59 2018', version: devel} 2 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/config_template/config_template/templates/fail.j2: -------------------------------------------------------------------------------- 1 | test {{ bad | default(omit) }} 2 | -------------------------------------------------------------------------------- /readme_pics/copy_flow_chart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colin-mccarthy/ansible_cisco_ios_upgrade/HEAD/readme_pics/copy_flow_chart.jpg -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/json_template/json_template/defaults/main.yaml: -------------------------------------------------------------------------------- 1 | template_path: "{{ role_path }}/templates" 2 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v262-filter-plugins.yaml: -------------------------------------------------------------------------------- 1 | new_filter_plugins: 2 | - NEW ``to_lines`` filter plugin 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/to_lines/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - to_lines 5 | -------------------------------------------------------------------------------- /readme_pics/install_flow_chart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colin-mccarthy/ansible_cisco_ios_upgrade/HEAD/readme_pics/install_flow_chart.jpg -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/netcfg_diff/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - netcfg_diff 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/vlan_expand/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - vlan_expand 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v266-removed-features.yaml: -------------------------------------------------------------------------------- 1 | removed_features: 2 | - Remove deprecated module ``cli_get`` 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - command_parser 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/config_template/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - config_template 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/interface_range/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - interface_range 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/interface_split/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - interface_split 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/json_template/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - json_template 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - textfsm_parser 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/vlan_compress/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - vlan_compress 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/.zuul.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | check: 3 | jobs: 4 | - ansible-test-sanity: 5 | voting: False 6 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/validate_role_spec/test.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | roles: 4 | - validate_role_spec 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v262-modules.yaml: -------------------------------------------------------------------------------- 1 | new_modules: 2 | - NEW ``validate_role_spec`` handle validating facts required by the role 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v262-lookup-plugins.yaml: -------------------------------------------------------------------------------- 1 | new_lookup_plugins: 2 | - NEW ``config_template`` lookup plugin 3 | - NEW ``yang_json2xml`` lookup plugin 4 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v251-docs.yaml: -------------------------------------------------------------------------------- 1 | docs: 2 | - User Guide `docs/user_guide `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v264-removed-features.yaml: -------------------------------------------------------------------------------- 1 | removed_features: 2 | - Remove deprecated module ``text_parser``. 3 | - Remove deprecated module ``textfsm``. 4 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v260-initial-release.yaml: -------------------------------------------------------------------------------- 1 | major_changes: 2 | - Initial release of 2.6.0 ``network-engine`` Ansible role that is supported with Ansible 2.6.0 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v262-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Fix role path test dependency `network-engine#121 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v264-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Fix repeat_for in json_template `network-engine#139 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v264-docs.yaml: -------------------------------------------------------------------------------- 1 | docs: 2 | - Removes unnecessary details from README `network-engine#126 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v261-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Fixes bug when loading a dir of parsers `network-engine#113 `_. 3 | -------------------------------------------------------------------------------- /roles/4500_install_image/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for 4500_install_image 3 | 4 | compliant_ios_version: 5 | compliant_ios_bytes: 6 | compliant_ios_md5: 7 | compliant_ios_image_name: 8 | ansible_command_timeout: 9 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v263-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Task to fail if ansible_min_version isn't met `network-engine#130 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v265-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Remove GenericLinux from supported platforms `network-engine#145 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v263-minorchanges.yaml: -------------------------------------------------------------------------------- 1 | minor_changes: 2 | - Makes parser directive extend templatable `network-engine#132 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v266-minor-changes.yaml: -------------------------------------------------------------------------------- 1 | minor_changes: 2 | - Capability to filter AnsibleModule kwargs `network-engine#149 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v252-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Fix AnsibleFilterError, deprecations, and unused imports `network-engine#82 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/defaults/main.yaml: -------------------------------------------------------------------------------- 1 | parser_path: "{{ role_path }}/parser_templates/{{ ansible_network_os }}" 2 | output_path: "{{ role_path }}/output/{{ ansible_network_os }}" 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/bindep.txt: -------------------------------------------------------------------------------- 1 | # This is a cross-platform list tracking distribution packages needed by tests; 2 | # see http://docs.openstack.org/infra/bindep/ for additional information. 3 | 4 | gcc-c++ [test platform:rpm] 5 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v261-docs.yaml: -------------------------------------------------------------------------------- 1 | docs: 2 | - The argument to end a block of text when searching with match_greedy was missing `network-engine#116 `_. 3 | -------------------------------------------------------------------------------- /4500_roles.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: UPGRADE 4500X FIRMWARE 4 | hosts: all 5 | connection: network_cli 6 | gather_facts: no 7 | 8 | roles: 9 | - ansible-network.network-engine 10 | - 4500_copy_image 11 | - 4500_install_image 12 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/defaults/main.yaml: -------------------------------------------------------------------------------- 1 | parser_path: "{{ role_path }}/parser_templates/{{ ansible_network_os }}" 2 | output_path: "{{ role_path }}/output/{{ ansible_network_os }}" 3 | export_type: "list" 4 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v252-lookup-plugins.yaml: -------------------------------------------------------------------------------- 1 | new_lookup_plugins: 2 | - New lookup plugin ``json_template`` 3 | - New lookup plugin ``network_template`` 4 | - New lookup plugin ``yang2spec`` 5 | - New lookup plugin ``netcfg_diff`` 6 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v252-filter-plugins.yaml: -------------------------------------------------------------------------------- 1 | new_filter_plugins: 2 | - New filter plugin ``interface_range`` 3 | - New filter plugin ``interface_split`` 4 | - New filter plugin ``vlan_compress`` 5 | - New filter plugin ``vlan_expand`` 6 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/to_lines/to_lines/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/to_lines/to_lines')[0] }}" 5 | 6 | - name: to_lines test 7 | import_tasks: to_lines.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v253-minorchanges.yaml: -------------------------------------------------------------------------------- 1 | minor_changes: 2 | - Templating the regex sent to the parser to allow us to use ansible variables in the regex string `network-engine#97 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v253-removed-features.yaml: -------------------------------------------------------------------------------- 1 | removed_features: 2 | - Move yang2spec lookup to feature branch, till the right location for this plugin is identified `network-engine#100 `_. 3 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/netcfg_diff/netcfg_diff/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/netcfg_diff/netcfg_diff')[0] }}" 5 | 6 | - name: ios config diff 7 | import_tasks: ios.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/to_lines/to_lines/tasks/to_lines.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: convert string to lines 3 | set_fact: 4 | test: "{{ 'test\nstring' | to_lines }}" 5 | 6 | - assert: 7 | that: 8 | - test[0] == 'test' 9 | - test[1] == 'string' 10 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/validate_role_spec/validate_role_spec/tasks/validate_role_spec.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - set_fact: 3 | required_arg: value 4 | int_arg: 10 5 | bool_arg: true 6 | optional_arg: value 7 | 8 | - validate_role_spec: 9 | spec: test.yaml 10 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/vlan_expand/vlan_expand/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/vlan_expand/vlan_expand')[0] }}" 5 | 6 | - name: vlan_expand test 7 | import_tasks: vlan_expand.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/command_parser/command_parser')[0] }}" 5 | 6 | - name: ios command_parser test 7 | import_tasks: ios.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/textfsm_parser/textfsm_parser')[0] }}" 5 | 6 | - name: ios textfsm_parser test 7 | import_tasks: ios.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/vlan_compress/vlan_compress/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/vlan_compress/vlan_compress')[0] }}" 5 | 6 | - name: vlan_compress test 7 | import_tasks: vlan_compress.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/config_template/config_template/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/config_template/config_template')[0] }}" 5 | 6 | - name: config_template test 7 | import_tasks: config_template.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/interface_range/interface_range/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/interface_range/interface_range')[0] }}" 5 | 6 | - name: interface_range test 7 | import_tasks: interface_range.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/interface_split/interface_split/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/interface_split/interface_split')[0] }}" 5 | 6 | - name: interface_split test 7 | import_tasks: interface_split.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/json_template/json_template/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/json_template/json_template')[0] }}" 5 | 6 | - name: json_template lookup plugin test 7 | import_tasks: json_lookup.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/netcfg_diff/netcfg_diff/defaults/main.yaml: -------------------------------------------------------------------------------- 1 | want_file: "{{ role_path }}/files/{{ ansible_network_os }}/want.txt" 2 | have_file: "{{ role_path }}/files/{{ ansible_network_os }}/have.txt" 3 | 4 | want: "{{ lookup('file', want_file) }}" 5 | have: "{{ lookup('file', have_file) }}" 6 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v254-minorchanges.yaml: -------------------------------------------------------------------------------- 1 | minor_changes: 2 | - Add parsers to search path `network-engine#89 `_. 3 | - Fix export_as templating vars `network-engine#104 `_. 4 | -------------------------------------------------------------------------------- /roles/4500_copy_image/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for 4500-upgrade 3 | 4 | 5 | compliant_ios_version: 03.08.05.E 6 | compliant_ios_bytes: 190492036 7 | compliant_ios_md5: 2b2fff9a4bf1dc08bbe48c3e9a03d17a 8 | compliant_ios_image_name: cat4500e-universalk9.SPA.03.08.05.E.152-4.E5.bin 9 | #ansible_command_timeout: 1200 10 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/validate_role_spec/validate_role_spec/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: import dependency role for test 3 | import_role: 4 | name: "{{ role_path.split('/tests/validate_role_spec/validate_role_spec')[0] }}" 5 | 6 | - name: validate_role_spec test 7 | import_tasks: validate_role_spec.yaml 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v252-minorchanges.yaml: -------------------------------------------------------------------------------- 1 | minor_changes: 2 | - Add new directives extend `network-engine#91 `_. 3 | - Adds conditional support to nested template objects `network-engine#55 `_. 4 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/parser_templates/ios/show_version: -------------------------------------------------------------------------------- 1 | Value version (\S+) 2 | Value model (.+) 3 | Value image (\S+) 4 | Value uptime (.+) 5 | 6 | 7 | Start 8 | ^.*Version ${version}, 9 | ^.*uptime is ${uptime} 10 | ^System image file is ${image} 11 | ^Cisco ${model} \(revision -> Record 12 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/parser_templates/ios/show_interfaces: -------------------------------------------------------------------------------- 1 | Value Required name (\S+) 2 | Value type ([\w ]+) 3 | Value description (.*) 4 | Value mtu (\d+) 5 | 6 | Start 7 | ^${name} is up 8 | ^\s+Hardware is ${type} -> Continue 9 | ^\s+Description: ${description} 10 | ^\s+MTU ${mtu} bytes, -> Record 11 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v254-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Fix cli task parser undefined issue when only command is used `network-engine#103 `_. 3 | - Fix an issue with using the extend directive with a loop `network-engine#105 `_. 4 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tasks/cli.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: initialize function 3 | include_tasks: includes/init.yaml 4 | 5 | - name: run command on remote network node and use engine to parse output into JSON facts 6 | cli: 7 | command: "{{ network_engine_command }}" 8 | parser: "{{ network_engine_parser | default(omit) }}" 9 | engine: "{{ network_engine_engine | default(omit) }}" 10 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/config_template/config_template/tasks/config_template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - set_fact: 3 | test: string 4 | 5 | - set_fact: 6 | test_pass: "{{ lookup('config_template', 'pass.j2') }}" 7 | test_fail: "{{ lookup('config_template', 'fail.j2') }}" 8 | 9 | - assert: 10 | that: 11 | - "'test string' in test_pass" 12 | - "'test string' not in test_fail" 13 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/validate_role_spec/validate_role_spec/meta/test.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | argument_spec: 3 | required_arg: 4 | required: true 5 | int_arg: 6 | type: int 7 | bool_arg: 8 | type: bool 9 | default_arg: 10 | default: test 11 | optional_arg: 12 | 13 | mutually_exclusive: 14 | - ['int_arg', 'missing_arg'] 15 | 16 | required_together: 17 | - ['default_arg', 'optional_arg'] 18 | -------------------------------------------------------------------------------- /parsers/4500_md5_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: dir 6 | network_os: ios 7 | 8 | - name: match md5 9 | pattern_match: 10 | regex: 'verify /md5 .*= (\S+)' 11 | register: md5 12 | 13 | - name: build md5 facts 14 | json_template: 15 | template: 16 | - key: md5 17 | value: "{{ md5.matches.0 }}" 18 | register: md5_facts 19 | export: true 20 | export_as: dict 21 | -------------------------------------------------------------------------------- /roles/4500_copy_image/parsers/4500_md5_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: dir 6 | network_os: ios 7 | 8 | - name: match md5 9 | pattern_match: 10 | regex: 'verify /md5 .*= (\S+)' 11 | register: md5 12 | 13 | - name: build md5 facts 14 | json_template: 15 | template: 16 | - key: md5 17 | value: "{{ md5.matches.0 }}" 18 | register: md5_facts 19 | export: true 20 | export_as: dict -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v251-bugfixes.yaml: -------------------------------------------------------------------------------- 1 | bugfixes: 2 | - Fix ``command_parser`` Absolute path with tilde in src should work `network-engine#58 `_ 3 | - Fix content mush only accepts string type `network-engine#72 `_ 4 | - Fix StringIO to work with Python3 in addition to Python2 `network-engine#53 `_ 5 | -------------------------------------------------------------------------------- /roles/4500_install_image/parsers/4500_md5_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: dir 6 | network_os: ios 7 | 8 | - name: match md5 9 | pattern_match: 10 | regex: 'verify /md5 .*= (\S+)' 11 | register: md5 12 | 13 | - name: build md5 facts 14 | json_template: 15 | template: 16 | - key: md5 17 | value: "{{ md5.matches.0 }}" 18 | register: md5_facts 19 | export: true 20 | export_as: dict -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v251-terminology-changes.yaml: -------------------------------------------------------------------------------- 1 | new_modules: 2 | - New module ``command_parser`` (renamed from ``text_parser``) 3 | - New module ``textfsm_parser`` (renamed from ``textfsm``) 4 | 5 | deprecated_features: 6 | - Module ``text_parser`` renamed to ``command_parser``; original name deprecated; legacy use supported; will be removed in 2.6.0. 7 | - Module ``textfsm`` renamed to ``textfsm_parser``; original name deprecated; legacy use supported; will be removed in 2.6.0. 8 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # main entrypoint 3 | 4 | - import_playbook: command_parser/test.yml 5 | - import_playbook: textfsm_parser/test.yml 6 | - import_playbook: json_template/test.yml 7 | - import_playbook: vlan_compress/test.yml 8 | - import_playbook: vlan_expand/test.yml 9 | - import_playbook: netcfg_diff/test.yml 10 | - import_playbook: interface_range/test.yml 11 | - import_playbook: interface_split/test.yml 12 | - import_playbook: config_template/test.yml 13 | - import_playbook: validate_role_spec/test.yml 14 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/json_template/json_template/templates/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": [ 3 | { 4 | "object": [ 5 | { 6 | "value": "GigabitEthernet0/0", 7 | "key": "name" 8 | }, 9 | { 10 | "value": "iGbE", 11 | "key": "type" 12 | }, 13 | { 14 | "value": "1500", 15 | "key": "mtu" 16 | }, 17 | { 18 | "value": "Configured by Ansible", 19 | "key": "description" 20 | } 21 | ], 22 | "key": "config" 23 | } 24 | ], 25 | "key": "GigabitEthernet0/0" 26 | } 27 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network-engine 3 | # 4 | network_engine_path: "{{ role_path }}" 5 | network_engine_lib_path: "{{ network_engine_path }}/lib" 6 | 7 | # defaults used in tasks/cli.yaml 8 | # 9 | # command to run on network device 10 | network_engine_command: "{{ command | default(None) }}" 11 | 12 | # the path to parser file 13 | network_engine_parser: "{{ parser | default(None) }}" 14 | 15 | # engine to use for parsing output to JSON facts 16 | network_engine_engine: "{{ engine | default('command_parser') }}" 17 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/plugins/template/normal.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | from network_engine.plugins.template import TemplateBase 11 | 12 | 13 | class TemplateEngine(TemplateBase): 14 | pass 15 | -------------------------------------------------------------------------------- /.github/workflows/ansible-lint.yml: -------------------------------------------------------------------------------- 1 | 2 | name: ansible-lint 3 | on: 4 | push: 5 | # schedule: 6 | # - cron: '0 0 1 * *' 7 | 8 | jobs: 9 | lint: 10 | name: ansible-lint 11 | runs-on: ubuntu-18.04 12 | 13 | steps: 14 | - name: Check out source 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up Python 3.8 18 | uses: actions/setup-python@v1 19 | with: 20 | python-version: 3.8 21 | 22 | - name: Install ansible-lint 23 | run: pip install ansible-lint 24 | 25 | 26 | - name: Run ansible-lint 27 | run: ansible-lint *.yml 28 | -------------------------------------------------------------------------------- /parsers/3560_dir_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | 4 | - name: files 5 | pattern_match: 6 | regex: 'c3560\S*' 7 | match_all: yes 8 | #content: "{{ item }}" 9 | register: files 10 | export: yes 11 | export_as: list 12 | 13 | 14 | - name: avail meme 15 | pattern_match: 16 | regex: '\((\S*) bytes free\)' 17 | register: meme 18 | 19 | 20 | 21 | - name: template values 22 | register: directory 23 | export: yes 24 | export_as: dict 25 | json_template: 26 | template: 27 | - key: files 28 | value: "{{ files | map(attribute='matches') | list }}" 29 | - key: bytes_free 30 | value: "{{ meme.matches.0 }}" 31 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/json_template/json_template/tasks/json_lookup.yaml: -------------------------------------------------------------------------------- 1 | - name: generate config 2 | debug: 3 | msg: "{{ lookup('json_template', '{{ template_path }}/config.json') }}" 4 | register: result 5 | 6 | - assert: 7 | that: 8 | - "'GigabitEthernet0/0' in result.msg" 9 | - "'config' in result['msg']['GigabitEthernet0/0']" 10 | - "'Configured by Ansible' in result['msg']['GigabitEthernet0/0']['config']['description']" 11 | - "result['msg']['GigabitEthernet0/0']['config']['mtu'] == '1500'" 12 | - "'iGbE' in result['msg']['GigabitEthernet0/0']['config']['type']" 13 | - "'GigabitEthernet0/0' in result['msg']['GigabitEthernet0/0']['config']['name']" 14 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/includes/init.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: read ansible_min_version from meta data 3 | include_vars: 4 | file: ../meta/main.yml 5 | name: ansible_galaxy_meta 6 | 7 | - name: validate minimum Ansible version 8 | fail: 9 | msg: "This role requires Ansible {{ ansible_galaxy_meta.galaxy_info.min_ansible_version }} or higher. You are currently running Ansible {{ ansible_version.full }}." 10 | when: 'ansible_version.full is version_compare(ansible_galaxy_meta.galaxy_info.min_ansible_version, "<")' 11 | 12 | - name: validate connection is network_cli 13 | fail: 14 | msg: "expected connection network_cli, got {{ ansible_connection }}" 15 | when: ansible_connection != 'network_cli' 16 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/interface_split/interface_split/tasks/interface_split.yaml: -------------------------------------------------------------------------------- 1 | - name: interface_split Ethernet1 2 | debug: 3 | msg: "{{ 'Ethernet1' | interface_split }}" 4 | register: result 5 | 6 | - assert: 7 | that: 8 | - "'1' in result.msg.index" 9 | - "'Ethernet' in result.msg.name" 10 | 11 | - name: interface_split Ethernet1 name 12 | debug: 13 | msg: "{{ 'Ethernet1' | interface_split('name') }}" 14 | register: result 15 | 16 | - assert: 17 | that: 18 | - "'Ethernet' in result.msg" 19 | 20 | - name: interface_split Ethernet1 index 21 | debug: 22 | msg: "{{ 'Ethernet1' | interface_split('index') }}" 23 | register: result 24 | 25 | - assert: 26 | that: 27 | - "'1' in result.msg" 28 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 1.6 3 | skipsdist = True 4 | envlist = linters,py27,py36 5 | 6 | [testenv] 7 | install_command = pip install {opts} {packages} 8 | commands = 9 | ansible-playbook -i tests/inventory tests/test.yml 10 | deps = 11 | -r{toxinidir}/requirements.txt 12 | -r{toxinidir}/test-requirements.txt 13 | setenv = 14 | ANSIBLE_CALLBACK_PLUGINS = {envsitepackagesdir}/ara/plugins/callbacks 15 | 16 | [testenv:linters] 17 | basepython = python3 18 | commands = 19 | flake8 {posargs} 20 | 21 | [testenv:venv] 22 | commands = {posargs} 23 | 24 | [flake8] 25 | # TODO(pabelanger): Follow sane flake8 rules for galaxy and sync across all of 26 | # ansible-network. 27 | ignore = E125,E129,E402 28 | max-line-length = 160 29 | show-source = True 30 | exclude = .venv,.tox,dist,doc,build,*.egg 31 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/vlan_compress/vlan_compress/tasks/vlan_compress.yaml: -------------------------------------------------------------------------------- 1 | - name: vlan_compress single vlan 2 | debug: 3 | msg: "{{ [1] | vlan_compress }}" 4 | register: result 5 | 6 | - assert: 7 | that: 8 | - "'1' in result.msg" 9 | 10 | - name: vlan_compress list of vlans1 11 | debug: 12 | msg: "{{ [1,2,3,4,5] | vlan_compress }}" 13 | register: result 14 | 15 | - assert: 16 | that: 17 | - "'1-5' in result.msg" 18 | 19 | - name: vlan_compress list of vlans2 20 | debug: 21 | msg: "{{ [1,2,3,5] | vlan_compress }}" 22 | register: result 23 | 24 | - assert: 25 | that: 26 | - "'1-3,5' in result.msg" 27 | 28 | - name: vlan_compress list of vlans3 29 | debug: 30 | msg: "{{ [1,2,4,5,6] | vlan_compress }}" 31 | register: result 32 | 33 | - assert: 34 | that: 35 | - "'1-2,4-6' in result.msg" 36 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | from ansible.plugins.loader import PluginLoader 11 | 12 | template_loader = PluginLoader( 13 | 'TemplateEngine', 14 | 'network_engine.plugins.template', 15 | None, 16 | 'template_plugins', 17 | required_base_class='TemplateBase' 18 | ) 19 | 20 | parser_loader = PluginLoader( 21 | 'ParserEngine', 22 | 'network_engine.plugins.parser', 23 | None, 24 | 'parser_plugins', 25 | # required_base_class='ParserBase' 26 | ) 27 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/vlan_expand/vlan_expand/tasks/vlan_expand.yaml: -------------------------------------------------------------------------------- 1 | - name: vlan_expand single vlan 2 | debug: 3 | msg: "{{ 'vlan1' | vlan_expand }}" 4 | register: result 5 | 6 | - assert: 7 | that: 8 | - "'1' in result.msg" 9 | 10 | - name: vlan_expand range of vlans1 11 | debug: 12 | msg: "{{ 'vlan1-5' | vlan_expand }}" 13 | register: result 14 | 15 | - assert: 16 | that: 17 | - "'1' in result.msg" 18 | - "'2' in result.msg" 19 | - "'3' in result.msg" 20 | - "'4' in result.msg" 21 | - "'5' in result.msg" 22 | 23 | - name: vlan_expand range of vlans2 24 | debug: 25 | msg: "{{ 'vlan1,3-5,7' | vlan_expand }}" 26 | register: result 27 | 28 | - assert: 29 | that: 30 | - "'1' in result.msg" 31 | - "'3' in result.msg" 32 | - "'4' in result.msg" 33 | - "'5' in result.msg" 34 | - "'7' in result.msg" 35 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/interface_range/interface_range/tasks/interface_range.yaml: -------------------------------------------------------------------------------- 1 | - name: interface_range Ethernet1-3 2 | debug: 3 | msg: "{{ 'Ethernet1-3' | interface_range }}" 4 | register: result 5 | 6 | - assert: 7 | that: 8 | - "'Ethernet1' in result.msg" 9 | - "'Ethernet2' in result.msg" 10 | - "'Ethernet3' in result.msg" 11 | 12 | - name: interface_range Ethernet1,3-4,5 13 | debug: 14 | msg: "{{ 'Ethernet1,3-4,5' | interface_range }}" 15 | register: result 16 | 17 | - assert: 18 | that: 19 | - "'Ethernet1' in result.msg" 20 | - "'Ethernet3' in result.msg" 21 | - "'Ethernet4' in result.msg" 22 | - "'Ethernet5' in result.msg" 23 | 24 | - name: interface_range Ethernet1/3-5,8 25 | debug: 26 | msg: "{{ 'Ethernet1/3-5,8' | interface_range }}" 27 | register: result 28 | 29 | - assert: 30 | that: 31 | - "'Ethernet1/3' in result.msg" 32 | - "'Ethernet1/4' in result.msg" 33 | - "'Ethernet1/5' in result.msg" 34 | - "'Ethernet1/8' in result.msg" 35 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # FIXME GUNDALOW: This file will need work once we have defined the Git Tag & branch strategy 3 | # Currently we've just copied ansible/ansible's changelogs/config.yaml 4 | release_tag_re: '(v(?:[\d.ab\-]|rc)+)' 5 | pre_release_tag_re: '(?P(?:[ab]|rc)+\d*)$' 6 | notesdir: fragments 7 | prelude_section_name: release_summary 8 | sections: 9 | - ['major_changes', 'Major Changes'] 10 | - ['minor_changes', 'Minor Changes'] 11 | - ['deprecated_features', 'Deprecated Features'] 12 | - ['removed_features', 'Removed Features (previously deprecated)'] 13 | - ['new_lookup_plugins', 'New Lookup Plugins'] 14 | - ['new_callback_plugins', 'New Callback Plugins'] 15 | - ['new_connection_plugins', 'New Connection Plugins'] 16 | - ['new_test_plugins', 'New Test Plugins'] 17 | - ['new_filter_plugins', 'New Filter Plugins'] 18 | - ['new_modules', 'New Modules'] 19 | - ['new_tasks', 'New Tasks'] 20 | - ['bugfixes', 'Bugfixes'] 21 | - ['known_issues', 'Known Issues'] 22 | - ['docs', 'Documentation Updates'] 23 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/changelogs/fragments/v0-initial-release.yaml: -------------------------------------------------------------------------------- 1 | major_changes: 2 | - Initial release of the ``network-engine`` Ansible role. 3 | - This role provides the foundation for building network roles by providing modules and plugins that are common to all Ansible Network roles. All of the artifacts in this role can be used independent of the platform that is being managed. 4 | 5 | new_modules: 6 | - NEW ``text_parser`` Parses ASCII text into JSON facts using text_parser engine and YAML-formatted input. 7 | Provides a rules-based text parser that is closely modeled after the Ansible 8 | playbook language. This parser will iterate over the rules and parse the 9 | output of structured ASCII text into a JSON data structure that can be 10 | added to the inventory host facts. 11 | - NEW ``textfsm`` Parses ASCII text into JSON facts using textfsm engine and Google TextFSM-formatted input. 12 | Provides textfsm rules-based templates to parse data from text. The template acting as parser will iterate of the rules and parse the output of structured ASCII text into a JSON data structure that can be added to the inventory host facts. 13 | -------------------------------------------------------------------------------- /roles/4500_copy_image/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for 4500-upgrade 3 | 4 | 5 | - name: SHOW VERSION 6 | ios_command: 7 | commands: 8 | - show version 9 | register: version 10 | 11 | - name: PARSE SHOW VERSION 12 | command_parser: 13 | file: "parsers/4500_version_parser.yml" 14 | content: "{{ version.stdout.0 }}" 15 | 16 | - name: RUN DIR 17 | ios_command: 18 | commands: 19 | - dir 20 | register: dir 21 | 22 | - name: PARSE DIR FILES 23 | command_parser: 24 | file: "parsers/4500_dir_parser.yml" 25 | content: "{{ dir.stdout.0 }}" 26 | 27 | - debug: var=dir_facts 28 | 29 | - name: FAIL IF NOT ENOUGH SPACE 30 | fail: 31 | msg: "Not enough space on filesystem" 32 | when: "(dir_facts.bytes_free < compliant_ios_bytes) and (item not in dir_facts.files)" 33 | with_items: "{{ compliant_ios_image_name }}" 34 | 35 | - name: COPY IOS IMAGE 36 | when: "(item not in dir_facts.files)" 37 | with_items: "{{ compliant_ios_image_name }}" 38 | ios_command: 39 | commands: 40 | - 'copy ftp://cmccarth:cmccarth@10.10.0.1/cat4500e-universalk9.SPA.03.08.05.E.152-4.E5.bin bootflash:cat4500e-universalk9.SPA.03.08.05.E.152-4.E5.bin\n' 41 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: ansible-network 4 | description: This role provides the foundation for building network roles by providing modules and plugins that are common to all Ansible Network roles. 5 | company: Red Hat 6 | 7 | issue_tracker_url: https://github.com/ansible-network/network-engine/issues 8 | 9 | license: GPLv3 10 | 11 | min_ansible_version: 2.7 12 | 13 | # If this a Container Enabled role, provide the minimum Ansible Container version. 14 | # min_ansible_container_version: 15 | 16 | # Optionally specify the branch Galaxy will use when accessing the GitHub 17 | # repo for this role. During role install, if no tags are available, 18 | # Galaxy will use this branch. During import Galaxy will access files on 19 | # this branch. If Travis integration is configured, only notifications for this 20 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 21 | # (usually master) will be used. 22 | #github_branch: 23 | 24 | platforms: 25 | - name: network 26 | versions: 27 | - all 28 | 29 | galaxy_tags: 30 | - ansible 31 | - network 32 | - networking 33 | - engine 34 | - core 35 | - cli 36 | 37 | dependencies: [] 38 | -------------------------------------------------------------------------------- /parsers/3560_show_version_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show version 6 | network_os: ios 7 | 8 | - name: match version 9 | pattern_match: 10 | regex: "Version (\\S+)," 11 | register: version 12 | 13 | - name: match model 14 | pattern_match: 15 | regex: "^Cisco (.+) \\(revision" 16 | register: model 17 | 18 | - name: match image 19 | pattern_match: 20 | regex: 'System image file is \"\S*:(.*)\"' 21 | register: image 22 | 23 | - name: match uptime 24 | pattern_match: 25 | regex: "uptime is (.+)" 26 | register: uptime 27 | 28 | - name: match total memory 29 | pattern_match: 30 | regex: "with (\\S+)/(\\w*) bytes of memory" 31 | register: total_mem 32 | 33 | - name: match free memory 34 | pattern_match: 35 | regex: "with \\w*/(\\S+) bytes of memory" 36 | register: free_mem 37 | 38 | - name: export system facts to playbook 39 | set_vars: 40 | model: "{{ model.matches.0 }}" 41 | image_file: "{{ image.matches.0 }}" 42 | uptime: "{{ uptime.matches.0 }}" 43 | version: "{{ version.matches.0 }}" 44 | memory: 45 | total: "{{ total_mem.matches.0 }}" 46 | free: "{{ free_mem.matches.0 }}" 47 | export: yes 48 | register: system_facts 49 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/parser_templates/ios/show_version.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show version 6 | network_os: ios 7 | 8 | - name: match version 9 | pattern_match: 10 | regex: "Version (\\S+)," 11 | register: version 12 | 13 | - name: match model 14 | pattern_match: 15 | regex: "^Cisco (.+) \\(revision" 16 | register: model 17 | 18 | - name: match image 19 | pattern_match: 20 | regex: "^System image file is (\\S+)" 21 | register: image 22 | 23 | - name: match uptime 24 | pattern_match: 25 | regex: "uptime is (.+)" 26 | register: uptime 27 | 28 | - name: match total memory 29 | pattern_match: 30 | regex: "with (\\S+)/(\\w*) bytes of memory" 31 | register: total_mem 32 | 33 | - name: match free memory 34 | pattern_match: 35 | regex: "with \\w*/(\\S+) bytes of memory" 36 | register: free_mem 37 | 38 | - name: export system facts to playbook 39 | set_vars: 40 | model: "{{ model.matches.0 }}" 41 | image_file: "{{ image.matches.0 }}" 42 | uptime: "{{ uptime.matches.0 }}" 43 | version: "{{ version.matches.0 }}" 44 | memory: 45 | total: "{{ total_mem.matches.0 }}" 46 | free: "{{ free_mem.matches.0 }}" 47 | export: yes 48 | register: system_facts 49 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # ISSUE TYPE 6 | 7 | 8 | 9 | - Bug Report 10 | - Feature Idea 11 | - Documentation Report 12 | 13 | # ANSIBLE VERSION 14 | 15 | ``` 16 | ansible --version 17 | 18 | ansible-galaxy list | grep ansible.network 19 | ``` 20 | 21 | # Network OS 22 | 23 | - Operating System (inc version) of machine running Ansible 24 | - Network device make/model, including version 25 | 26 | # SUMMARY 27 | 28 | 29 | 30 | # STEPS TO REPRODUCE 31 | 32 | 35 | 36 | ```yaml 37 | 38 | ``` 39 | 40 | 41 | 42 | # EXPECTED RESULTS 43 | 44 | 45 | 46 | # ACTUAL RESULTS 47 | 48 | 50 | 51 | ``` 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/parser_templates/ios/show_version_expand.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show version 6 | network_os: ios 7 | 8 | - name: match version 9 | pattern_match: 10 | regex: "Version (\\S+)," 11 | register: version 12 | 13 | - name: match model 14 | pattern_match: 15 | regex: "^Cisco (.+) \\(revision" 16 | register: model 17 | 18 | - name: match image 19 | pattern_match: 20 | regex: "^System image file is (\\S+)" 21 | register: image 22 | 23 | - name: match uptime 24 | pattern_match: 25 | regex: "uptime is (.+)" 26 | register: uptime 27 | 28 | - name: match total memory 29 | pattern_match: 30 | regex: "with (\\S+)/(\\w*) bytes of memory" 31 | register: total_mem 32 | 33 | - name: match free memory 34 | pattern_match: 35 | regex: "with \\w*/(\\S+) bytes of memory" 36 | register: free_mem 37 | 38 | - name: export system facts to playbook 39 | set_vars: 40 | model: "{{ model.matches.0 }}" 41 | image_file: "{{ image.matches.0 }}" 42 | uptime: "{{ uptime.matches.0 }}" 43 | version: "{{ version.matches.0 }}" 44 | memory: 45 | total: "{{ total_mem.matches.0 }}" 46 | free: "{{ free_mem.matches.0 }}" 47 | export: yes 48 | register: system_facts 49 | extend: test.extension 50 | -------------------------------------------------------------------------------- /parsers/4500_dir_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: dir 6 | network_os: ios 7 | 8 | - name: match files 9 | pattern_match: 10 | regex: '\d+\s+-\S+\s+\d+\s+\S+\s\d+\s\d+\s\S+\s+\S+\s+(\S+)' 11 | match_all: yes 12 | register: files 13 | 14 | - name: match directories 15 | pattern_match: 16 | regex: '\d+\s+d\S+\s+\d+\s+\S+\s\d+\s\d+\s\S+\s+\S+\s+(\S+)' 17 | match_all: yes 18 | register: directories 19 | 20 | - name: match bytes total 21 | pattern_match: 22 | regex: '^(\d+) bytes total' 23 | register: bytes_total 24 | 25 | - name: match bytes free 26 | pattern_match: 27 | regex: '(\d+) bytes free' 28 | register: bytes_free 29 | 30 | - name: match filesytem 31 | pattern_match: 32 | regex: '^Directory of (\S+)/' 33 | register: filesytem 34 | 35 | - name: build filesytem facts 36 | json_template: 37 | template: 38 | - key: filesystem 39 | value: "{{ filesytem.matches.0 }}" 40 | - key: files 41 | value: "{{ files | map(attribute='matches') | list }}" 42 | - key: directories 43 | value: "{{ directories | map(attribute='matches') | list }}" 44 | - key: bytes_total 45 | value: "{{ bytes_total.matches.0 }}" 46 | - key: bytes_free 47 | value: "{{ bytes_free.matches.0 }}" 48 | register: dir_facts 49 | export: true 50 | export_as: dict 51 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/tests/test_guide.md: -------------------------------------------------------------------------------- 1 | # Test Guide 2 | 3 | The tests in network-engine are role based where the entry point is `tests/test.yml`. 4 | The tests for `textfsm_parser` and `command_parser` are run against `localhost`. 5 | 6 | ## How to run tests locally 7 | 8 | ``` 9 | cd tests/ 10 | ansible-playbook -i inventory test.yml 11 | ``` 12 | 13 | ## Role Structure 14 | 15 | ``` 16 | role_name 17 | ├── defaults 18 | │   └── main.yaml 19 | ├── meta 20 | │   └── main.yaml 21 | ├── output 22 | │   └── platform_name 23 | │   ├── show_interfaces.txt 24 | │   └── show_version.txt 25 | ├── parser_templates 26 | │   └── platform_name 27 | │   ├── show_interfaces.yaml 28 | │   └── show_version.yaml 29 | └── tasks 30 | ├── platform_name.yaml 31 | └── main.yaml 32 | ``` 33 | 34 | If you add any new Role for test, make sure to include the role in `test.yml`: 35 | 36 | ```yaml 37 | 38 | roles: 39 | - command_parser 40 | - textfsm_parser 41 | - $role_name 42 | ``` 43 | 44 | ## Add new platforms tests to an existing roles 45 | 46 | Create directory with the `platform_name` in `output` and `parser_templates` directories 47 | which will contain output and parser files of the platform. 48 | 49 | Add corresponding playbook with the `platform_name` in `tasks/$platform_name.yaml` 50 | and add an entry in `tasks/main.yaml`: 51 | 52 | ```yaml 53 | - name: platform_name command_parser test 54 | import_tasks: platform_name.yaml 55 | ``` 56 | -------------------------------------------------------------------------------- /roles/4500_copy_image/parsers/4500_dir_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: dir 6 | network_os: ios 7 | 8 | - name: match files 9 | pattern_match: 10 | regex: '\d+\s+-\S+\s+\d+\s+\S+\s\d+\s\d+\s\S+\s+\S+\s+(\S+)' 11 | match_all: yes 12 | register: files 13 | 14 | - name: match directories 15 | pattern_match: 16 | regex: '\d+\s+d\S+\s+\d+\s+\S+\s\d+\s\d+\s\S+\s+\S+\s+(\S+)' 17 | match_all: yes 18 | register: directories 19 | 20 | - name: match bytes total 21 | pattern_match: 22 | regex: '^(\d+) bytes total' 23 | register: bytes_total 24 | 25 | - name: match bytes free 26 | pattern_match: 27 | regex: '(\d+) bytes free' 28 | register: bytes_free 29 | 30 | - name: match filesytem 31 | pattern_match: 32 | regex: '^Directory of (\S+)/' 33 | register: filesytem 34 | 35 | - name: build filesytem facts 36 | json_template: 37 | template: 38 | - key: filesystem 39 | value: "{{ filesytem.matches.0 }}" 40 | - key: files 41 | value: "{{ files | map(attribute='matches') | list }}" 42 | - key: directories 43 | value: "{{ directories | map(attribute='matches') | list }}" 44 | - key: bytes_total 45 | value: "{{ bytes_total.matches.0 }}" 46 | - key: bytes_free 47 | value: "{{ bytes_free.matches.0 }}" 48 | register: dir_facts 49 | export: true 50 | export_as: dict -------------------------------------------------------------------------------- /roles/4500_install_image/parsers/4500_dir_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: dir 6 | network_os: ios 7 | 8 | - name: match files 9 | pattern_match: 10 | regex: '\d+\s+-\S+\s+\d+\s+\S+\s\d+\s\d+\s\S+\s+\S+\s+(\S+)' 11 | match_all: yes 12 | register: files 13 | 14 | - name: match directories 15 | pattern_match: 16 | regex: '\d+\s+d\S+\s+\d+\s+\S+\s\d+\s\d+\s\S+\s+\S+\s+(\S+)' 17 | match_all: yes 18 | register: directories 19 | 20 | - name: match bytes total 21 | pattern_match: 22 | regex: '^(\d+) bytes total' 23 | register: bytes_total 24 | 25 | - name: match bytes free 26 | pattern_match: 27 | regex: '(\d+) bytes free' 28 | register: bytes_free 29 | 30 | - name: match filesytem 31 | pattern_match: 32 | regex: '^Directory of (\S+)/' 33 | register: filesytem 34 | 35 | - name: build filesytem facts 36 | json_template: 37 | template: 38 | - key: filesystem 39 | value: "{{ filesytem.matches.0 }}" 40 | - key: files 41 | value: "{{ files | map(attribute='matches') | list }}" 42 | - key: directories 43 | value: "{{ directories | map(attribute='matches') | list }}" 44 | - key: bytes_total 45 | value: "{{ bytes_total.matches.0 }}" 46 | - key: bytes_free 47 | value: "{{ bytes_free.matches.0 }}" 48 | register: dir_facts 49 | export: true 50 | export_as: dict -------------------------------------------------------------------------------- /roles/4500_copy_image/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /roles/4500_install_image/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/ANNOUNCE.md: -------------------------------------------------------------------------------- 1 | Ansible Network: Network Engine Release 2 | ---------------------------------------- 3 | 4 | The Ansible Network team is pleased to announce that the initial release of the Network Engine Ansible role is now available in Ansible Galaxy! 5 | 6 | What is an Ansible Role? 7 | ---------------------------------- 8 | An Ansible Role is a collection of related tasks, methods, plugins, and modules in a standard format. You can use Roles in tasks or playbooks. 9 | 10 | What does the Network Engine Role do? 11 | ---------------------------------- 12 | The Network Engine Role provides the fundamental building blocks for a data-model-driven approach to automated network management. Network Engine: 13 | 14 | - extracts data about your network devices 15 | - returns the data as Ansible facts in a JSON data structure, ready to be added to your inventory host facts and/or consumed by Ansible tasks and templates 16 | - works on any network platform 17 | 18 | With the Network Engine role, and other Roles built around it, you can normalize your Ansible facts across your entire network. 19 | 20 | How do I get it? 21 | ---------------------------------- 22 | Via Ansible Galaxy using the following Linux command: 23 | 24 | `ansible-galaxy install ansible-network.network-engine` 25 | 26 | How do I use it? 27 | ---------------------------------- 28 | See the [User Guide](https://github.com/ansible-network/network-engine/blob/devel/docs/user_guide/README.md) for details and examples. 29 | 30 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/library/net_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # (c) 2018, Red Hat, Inc. 5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 | 7 | ANSIBLE_METADATA = {'metadata_version': '1.1', 8 | 'status': ['preview'], 9 | 'supported_by': 'network'} 10 | 11 | 12 | DOCUMENTATION = """ 13 | --- 14 | module: net_facts 15 | version_added: "2.7" 16 | short_description: Collect device capabilities from Network devices 17 | description: 18 | - Collect basic fact capabilities from Network devices and return 19 | the capabilities as Ansible facts. 20 | 21 | author: 22 | - Trishna Guha (@trishnaguha) 23 | options: {} 24 | """ 25 | 26 | EXAMPLES = """ 27 | - facts: 28 | """ 29 | 30 | RETURN = """ 31 | """ 32 | from ansible.module_utils.basic import AnsibleModule 33 | from ansible.module_utils.connection import Connection 34 | 35 | 36 | def main(): 37 | """ main entry point for Ansible module 38 | """ 39 | argument_spec = {} 40 | 41 | module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) 42 | 43 | connection = Connection(module._socket_path) 44 | facts = connection.get_capabilities() 45 | facts = module.from_json(facts) 46 | result = { 47 | 'changed': False, 48 | 'ansible_facts': {'ansible_network_facts': {'capabilities': facts['device_info']}} 49 | } 50 | module.exit_json(**result) 51 | 52 | 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example of Cisco image upgrade with Ansible 2 | ============================================ 3 | 4 | This repo contains examples of playbooks and roles that upgrade an image on a switch using the [ios_command module][1] and [ansible-network.network_engine][3]. 5 | 6 | One of the use cases for Ansible and the [networking modules][2] is to grab information in real time from the network. These tasks will do just that, including verify available disk space as well as which files the device currently has on flash. 7 | 8 | ![Cisco 4500-X][6] 9 | 10 | 11 | Copy Role 12 | -------------- 13 | 14 | ![copy_flow][8] 15 | 16 | 17 | Install Role 18 | -------------- 19 | 20 | ![install_flow][9] 21 | 22 | 23 | 4500_roles.yml 24 | ------------ 25 | 26 | ``` 27 | --- 28 | - name: UPGRADE 4500X FIRMWARE 29 | hosts: all 30 | connection: network_cli 31 | gather_facts: no 32 | roles: 33 | - ansible-network.network-engine 34 | - 4500_copy_image 35 | - 4500_install_image 36 | 37 | ``` 38 | --------------- 39 | 40 | 41 | [1]:https://docs.ansible.com/ansible/latest/modules/ios_command_module.html#ios-command-module 42 | [2]: http://docs.ansible.com/ansible/latest/list_of_network_modules.html 43 | [3]: https://github.com/ansible-network/network-engine 44 | [4]: http://docs.ansible.com/ansible/latest/ios_facts_module.html 45 | [5]: http://docs.ansible.com/ansible/latest/template_module.html 46 | [6]: readme_pics/4500-X.jpg 47 | [7]: https://github.com/ansible/ansible-lint 48 | [8]: readme_pics/copy_flow_chart.jpg 49 | [9]: readme_pics/install_flow_chart.jpg 50 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lookup_plugins/config_template.py: -------------------------------------------------------------------------------- 1 | # (c) 2012, Michael DeHaan 2 | # (c) 2012-17 Ansible Project 3 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 4 | # 5 | # You should have received a copy of the GNU General Public License 6 | # along with Ansible. If not, see . 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | DOCUMENTATION = """ 11 | lookup: config_template 12 | author: Peter Sprygada (privateip) 13 | version_added: "2.7" 14 | short_description: retrieve contents of file after templating with Jinja2 15 | description: 16 | - This lookup plugin implements the standard template plugin with a slight 17 | twist in that it supports using default(omit) to remove an entire line 18 | options: 19 | _terms: 20 | description: list of files to template 21 | """ 22 | 23 | EXAMPLES = """ 24 | - name: show templating results 25 | debug: msg="{{ lookup('config_template', './some_template.j2') }} 26 | """ 27 | 28 | RETURN = """ 29 | _raw: 30 | description: file(s) content after templating 31 | """ 32 | from ansible.plugins.lookup.template import LookupModule as LookupBase 33 | 34 | 35 | class LookupModule(LookupBase): 36 | 37 | def run(self, terms, variables, **kwargs): 38 | 39 | ret = super(LookupModule, self).run(terms, variables, **kwargs) 40 | 41 | omit = variables['omit'] 42 | filtered = list() 43 | 44 | for line in ret[0].split('\n'): 45 | if all((line, omit not in line, not line.startswith('!'))): 46 | filtered.append(line) 47 | 48 | return [filtered] 49 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/parser_templates/ios/show_interfaces.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show interface 6 | network_os: ios 7 | 8 | - name: match sections 9 | pattern_match: 10 | regex: "^(\\S+) is up," 11 | match_all: yes 12 | match_greedy: yes 13 | register: section 14 | 15 | - name: match interface values 16 | pattern_group: 17 | - name: match name 18 | pattern_match: 19 | regex: "^(\\S+)" 20 | content: "{{ item }}" 21 | register: name 22 | 23 | - name: match hardware 24 | pattern_match: 25 | regex: "Hardware is (\\S+)," 26 | content: "{{ item }}" 27 | register: type 28 | 29 | - name: match mtu 30 | pattern_match: 31 | regex: "MTU (\\d+)" 32 | content: "{{ item }}" 33 | register: mtu 34 | 35 | - name: match description 36 | pattern_match: 37 | regex: "Description: (.*)" 38 | content: "{{ item }}" 39 | register: description 40 | loop: "{{ section }}" 41 | register: interfaces 42 | 43 | - name: generate json data structure 44 | json_template: 45 | template: 46 | - key: "{{ item.name.matches.0 }}" 47 | object: 48 | - key: config 49 | object: 50 | - key: name 51 | value: "{{ item.name.matches.0 }}" 52 | - key: type 53 | value: "{{ item.type.matches.0 }}" 54 | - key: mtu 55 | value: "{{ item.mtu.matches.0 }}" 56 | - key: description 57 | value: "{{ item.description.matches.0 }}" 58 | loop: "{{ interfaces }}" 59 | export: yes 60 | export_as: "{{ export_type }}" 61 | register: interface_facts 62 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/parser_templates/ios/show_interfaces_expand.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show interface 6 | network_os: ios 7 | 8 | - name: match sections 9 | pattern_match: 10 | regex: "^(\\S+) is up," 11 | match_all: yes 12 | match_greedy: yes 13 | register: section 14 | 15 | - name: match interface values 16 | pattern_group: 17 | - name: match name 18 | pattern_match: 19 | regex: "^(\\S+)" 20 | content: "{{ item }}" 21 | register: name 22 | 23 | - name: match hardware 24 | pattern_match: 25 | regex: "Hardware is (\\S+)," 26 | content: "{{ item }}" 27 | register: type 28 | 29 | - name: match mtu 30 | pattern_match: 31 | regex: "MTU (\\d+)" 32 | content: "{{ item }}" 33 | register: mtu 34 | 35 | - name: match description 36 | pattern_match: 37 | regex: "Description: (.*)" 38 | content: "{{ item }}" 39 | register: description 40 | loop: "{{ section }}" 41 | register: interfaces 42 | 43 | - name: generate json data structure 44 | json_template: 45 | template: 46 | - key: "{{ item.name.matches.0 }}" 47 | object: 48 | - key: config 49 | object: 50 | - key: name 51 | value: "{{ item.name.matches.0 }}" 52 | - key: type 53 | value: "{{ item.type.matches.0 }}" 54 | - key: mtu 55 | value: "{{ item.mtu.matches.0 }}" 56 | - key: description 57 | value: "{{ item.description.matches.0 }}" 58 | loop: "{{ interfaces }}" 59 | export: yes 60 | register: interface_facts 61 | extend: test.extension 62 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/output/ios/show_version.txt: -------------------------------------------------------------------------------- 1 | Cisco IOS Software, IOSv Software (VIOS-ADVENTERPRISEK9-M), Version 15.6(2)T, RELEASE SOFTWARE (fc2) 2 | Technical Support: http://www.cisco.com/techsupport 3 | Copyright (c) 1986-2016 by Cisco Systems, Inc. 4 | Compiled Tue 22-Mar-16 16:19 by prod_rel_team 5 | 6 | 7 | ROM: Bootstrap program is IOSv 8 | 9 | an-ios-01 uptime is 10 weeks, 6 days, 22 hours, 30 minutes 10 | System returned to ROM by reload 11 | System image file is "flash0:/vios-adventerprisek9-m" 12 | Last reload reason: Unknown reason 13 | 14 | 15 | 16 | This product contains cryptographic features and is subject to United 17 | States and local country laws governing import, export, transfer and 18 | use. Delivery of Cisco cryptographic products does not imply 19 | third-party authority to import, export, distribute or use encryption. 20 | Importers, exporters, distributors and users are responsible for 21 | compliance with U.S. and local country laws. By using this product you 22 | agree to comply with applicable laws and regulations. If you are unable 23 | to comply with U.S. and local laws, return this product immediately. 24 | 25 | A summary of U.S. laws governing Cisco cryptographic products may be found at: 26 | http://www.cisco.com/wwl/export/crypto/tool/stqrg.html 27 | 28 | If you require further assistance please contact us by sending email to 29 | export@cisco.com. 30 | 31 | Cisco IOSv (revision 1.0) with with 460033K/62464K bytes of memory. 32 | Processor board ID 92O0KON393UV5P77JRKZ5 33 | 4 Gigabit Ethernet interfaces 34 | DRAM configuration is 72 bits wide with parity disabled. 35 | 256K bytes of non-volatile configuration memory. 36 | 2097152K bytes of ATA System CompactFlash 0 (Read/Write) 37 | 0K bytes of ATA CompactFlash 1 (Read/Write) 38 | 0K bytes of ATA CompactFlash 2 (Read/Write) 39 | 10080K bytes of ATA CompactFlash 3 (Read/Write) 40 | 41 | 42 | 43 | Configuration register is 0x0 44 | 45 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/output/ios/show_version.txt: -------------------------------------------------------------------------------- 1 | Cisco IOS Software, IOSv Software (VIOS-ADVENTERPRISEK9-M), Version 15.6(2)T, RELEASE SOFTWARE (fc2) 2 | Technical Support: http://www.cisco.com/techsupport 3 | Copyright (c) 1986-2016 by Cisco Systems, Inc. 4 | Compiled Tue 22-Mar-16 16:19 by prod_rel_team 5 | 6 | 7 | ROM: Bootstrap program is IOSv 8 | 9 | an-ios-01 uptime is 10 weeks, 6 days, 22 hours, 30 minutes 10 | System returned to ROM by reload 11 | System image file is "flash0:/vios-adventerprisek9-m" 12 | Last reload reason: Unknown reason 13 | 14 | 15 | 16 | This product contains cryptographic features and is subject to United 17 | States and local country laws governing import, export, transfer and 18 | use. Delivery of Cisco cryptographic products does not imply 19 | third-party authority to import, export, distribute or use encryption. 20 | Importers, exporters, distributors and users are responsible for 21 | compliance with U.S. and local country laws. By using this product you 22 | agree to comply with applicable laws and regulations. If you are unable 23 | to comply with U.S. and local laws, return this product immediately. 24 | 25 | A summary of U.S. laws governing Cisco cryptographic products may be found at: 26 | http://www.cisco.com/wwl/export/crypto/tool/stqrg.html 27 | 28 | If you require further assistance please contact us by sending email to 29 | export@cisco.com. 30 | 31 | Cisco IOSv (revision 1.0) with with 460033K/62464K bytes of memory. 32 | Processor board ID 92O0KON393UV5P77JRKZ5 33 | 4 Gigabit Ethernet interfaces 34 | DRAM configuration is 72 bits wide with parity disabled. 35 | 256K bytes of non-volatile configuration memory. 36 | 2097152K bytes of ATA System CompactFlash 0 (Read/Write) 37 | 0K bytes of ATA CompactFlash 1 (Read/Write) 38 | 0K bytes of ATA CompactFlash 2 (Read/Write) 39 | 10080K bytes of ATA CompactFlash 3 (Read/Write) 40 | 41 | 42 | 43 | Configuration register is 0x0 44 | 45 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/library/command_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright 2018 Red Hat 5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 | 7 | from __future__ import absolute_import, division, print_function 8 | __metaclass__ = type 9 | 10 | 11 | ANSIBLE_METADATA = {'metadata_version': '1.1', 12 | 'status': ['preview'], 13 | 'supported_by': 'network'} 14 | 15 | 16 | DOCUMENTATION = ''' 17 | --- 18 | module: command_parser 19 | short_description: Parses text into JSON facts based on rules 20 | description: 21 | - Provides a rules base text parser that is closely modeled after the Ansible 22 | playbook language. This parser will iterate of the rules and parse the 23 | output of structured ASCII text into a JSON data structure that can be 24 | added to the inventory host facts. 25 | version_added: "2.5" 26 | options: 27 | dir: 28 | description: 29 | - The path to the directory that contains the parsers. The module will 30 | load all parsers found in this directory and pass the content through 31 | the them. This argument is mutually exclusive with C(file). 32 | default: null 33 | file: 34 | description: 35 | - The path to the parser to load from disk on the Ansible 36 | controller. This can be either the absolute path or relative path. 37 | This argument is mutually exclusive with C(dir). 38 | Default path is {{ playbook_dir }}/parser_templates/{{ ansible_network_os }} 39 | or {{ playbook_dir }}/parser_templates or {{ playbook_dir }} 40 | default: "{{ playbook_dir }}/parser_templates/{{ ansible_network_os }}" 41 | content: 42 | description: 43 | - The text content to pass to the parser engine. This argument provides 44 | the input to the text parser for generating the JSON data. 45 | required: true 46 | author: 47 | - Ansible Network Team 48 | ''' 49 | 50 | EXAMPLES = ''' 51 | - command_parser: 52 | file: files/parser_templates/show_interface.yaml 53 | content: "{{ lookup('file', 'output/show_interfaces.txt') }}" 54 | ''' 55 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/tasks/ios.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: textfsm_parser test for {{ ansible_network_os }} show_interfaces 3 | textfsm_parser: 4 | file: "{{ parser_path }}/show_interfaces" 5 | content: "{{ lookup('file', '{{ output_path }}/show_interfaces.txt') }}" 6 | name: interface_facts 7 | register: result 8 | vars: 9 | - ansible_network_os: ios 10 | 11 | - assert: 12 | that: 13 | - "'interface_facts' in result.ansible_facts" 14 | - "result.ansible_facts.interface_facts[0]['name'] == 'GigabitEthernet0/0'" 15 | - "result.ansible_facts.interface_facts[0]['mtu'] == '1500'" 16 | - "result.ansible_facts.interface_facts[0]['description'] == 'OOB Management'" 17 | - "result.ansible_facts.interface_facts[0]['type'] == 'iGbE'" 18 | - "result.ansible_facts.interface_facts[1]['name'] == 'GigabitEthernet0/1'" 19 | - "result.ansible_facts.interface_facts[1]['mtu'] == '2000'" 20 | - "result.ansible_facts.interface_facts[1]['description'] == 'test-interface'" 21 | - "result.ansible_facts.interface_facts[1]['type'] == 'iGbE'" 22 | - "result.ansible_facts.interface_facts[2]['name'] == 'GigabitEthernet0/2'" 23 | - "result.ansible_facts.interface_facts[2]['mtu'] == '2000'" 24 | - "result.ansible_facts.interface_facts[2]['description'] == 'test-interface-2'" 25 | - "result.ansible_facts.interface_facts[2]['type'] == 'iGbE'" 26 | 27 | - name: textfsm_parser test for {{ ansible_network_os }} show_version 28 | textfsm_parser: 29 | file: "{{ parser_path }}/show_version" 30 | content: "{{ lookup('file', '{{ output_path }}/show_version.txt') }}" 31 | name: system_facts 32 | register: result 33 | vars: 34 | - ansible_network_os: ios 35 | 36 | - assert: 37 | that: 38 | - "'system_facts' in result.ansible_facts" 39 | - "'flash0:/vios-adventerprisek9-m' in result.ansible_facts.system_facts[0]['image']" 40 | - "result.ansible_facts.system_facts[0]['model'] == 'IOSv'" 41 | - "result.ansible_facts.system_facts[0]['uptime'] == '10 weeks, 6 days, 22 hours, 30 minutes'" 42 | - "result.ansible_facts.system_facts[0]['version'] == '15.6(2)T'" 43 | -------------------------------------------------------------------------------- /roles/4500_copy_image/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Colin McCarthy 3 | description: roll your own 4 | company: Hoolie 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 2.4 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | # github_branch: 31 | 32 | # 33 | # Provide a list of supported platforms, and for each platform a list of versions. 34 | # If you don't wish to enumerate all versions for a particular platform, use 'all'. 35 | # To view available platforms and versions (or releases), visit: 36 | # https://galaxy.ansible.com/api/v1/platforms/ 37 | # 38 | platforms: 39 | - name: Cisco 40 | versions: 41 | - ios 42 | # - 25 43 | # - name: SomePlatform 44 | # versions: 45 | # - all 46 | # - 1.0 47 | # - 7 48 | # - 99.99 49 | 50 | galaxy_tags: [] 51 | # List tags for your role here, one per line. A tag is a keyword that describes 52 | # and categorizes the role. Users find roles by searching for tags. Be sure to 53 | # remove the '[]' above, if you add tags to this list. 54 | # 55 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 56 | # Maximum 20 tags per role. 57 | 58 | dependencies: [] 59 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 60 | # if you add dependencies to this list. 61 | -------------------------------------------------------------------------------- /roles/4500_install_image/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Colin McCarthy 3 | description: roll your own 4 | company: Hoolie 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 2.4 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # Provide a list of supported platforms, and for each platform a list of versions. 34 | # If you don't wish to enumerate all versions for a particular platform, use 'all'. 35 | # To view available platforms and versions (or releases), visit: 36 | # https://galaxy.ansible.com/api/v1/platforms/ 37 | # 38 | platforms: 39 | - name: Fedora 40 | versions: 41 | - ios 42 | # - 25 43 | # - name: SomePlatform 44 | # versions: 45 | # - all 46 | # - 1.0 47 | # - 7 48 | # - 99.99 49 | 50 | galaxy_tags: [] 51 | # List tags for your role here, one per line. A tag is a keyword that describes 52 | # and categorizes the role. Users find roles by searching for tags. Be sure to 53 | # remove the '[]' above, if you add tags to this list. 54 | # 55 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 56 | # Maximum 20 tags per role. 57 | 58 | dependencies: [] 59 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 60 | # if you add dependencies to this list. 61 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/netcfg_diff/netcfg_diff/tasks/ios.yaml: -------------------------------------------------------------------------------- 1 | - name: "config diff test for {{ ansible_network_os }} with default" 2 | set_fact: 3 | diff: "{{ lookup('netcfg_diff', want, have=have) }}" 4 | vars: 5 | - ansible_network_os: ios 6 | 7 | - assert: 8 | that: 9 | - "'version 15.6' in diff" 10 | - "'vrf definition Mgmt-intf\naddress-family ipv4' in diff" 11 | - "'crypto pki trustpoint example.com\nrevocation-check none\nrsakeypair example.com' not in diff" 12 | - "'no logging console' not in diff" 13 | 14 | - name: "config diff test for {{ ansible_network_os }} with strict match" 15 | set_fact: 16 | diff: "{{ lookup('netcfg_diff', want, have=have, match='strict') }}" 17 | vars: 18 | - ansible_network_os: ios 19 | 20 | - assert: 21 | that: 22 | - "'crypto pki trustpoint example.com\nrevocation-check none\nrsakeypair example.com' in diff" 23 | - "'no logging console' not in diff" 24 | 25 | - name: "config diff test for {{ ansible_network_os }} with exact match" 26 | set_fact: 27 | diff: "{{ lookup('netcfg_diff', want, have=have, match='exact') }}" 28 | vars: 29 | - ansible_network_os: ios 30 | 31 | - assert: 32 | that: 33 | - "'no logging console' in diff" 34 | 35 | - name: "config diff test for {{ ansible_network_os }} with block replace" 36 | set_fact: 37 | diff: "{{ lookup('netcfg_diff', want, have=have, replace='block') }}" 38 | vars: 39 | - ansible_network_os: ios 40 | 41 | - assert: 42 | that: 43 | - "'interface GigabitEthernet0/1' in diff" 44 | - "'description test-interface' in diff" 45 | - "'mtu 2000' in diff" 46 | - "'no ip address' in diff" 47 | - "'ip ospf cost 1' in diff" 48 | - "'shutdown' in diff" 49 | - "'duplex full' in diff" 50 | - "'speed 1000' in diff" 51 | - "'media-type rj45' in diff" 52 | 53 | - name: "config diff test for {{ ansible_network_os }} with block line" 54 | set_fact: 55 | diff: "{{ lookup('netcfg_diff', want, have=have, replace='line') }}" 56 | vars: 57 | - ansible_network_os: ios 58 | 59 | - assert: 60 | that: 61 | - "'interface GigabitEthernet0/1' in diff" 62 | - "'ip ospf cost 1' in diff" 63 | - "'shutdown' in diff" 64 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/plugins/filter_plugins.md: -------------------------------------------------------------------------------- 1 | # network_engine filter plugins 2 | 3 | The [filter_plugins/network_engine code](https://github.com/ansible-network/network-engine/blob/devel/library/filter_plugins/network_engine.py) 4 | offers four options for managing multiple interfaces and vlans. 5 | 6 | ## interface_split 7 | 8 | The `interface_split` plugin splits an interface and returns its parts: 9 | 10 | {{ 'Ethernet1' | interface_split }} returns '1' as index and 'Ethernet' as name 11 | 12 | {{ 'Ethernet1' | interface_split('name') }} returns 'Ethernet' 13 | 14 | {{ 'Ethernet1' | interface_split('index') }} returns '1' 15 | 16 | [interface_split tests](https://github.com/ansible-network/network-engine/blob/devel/tests/interface_split/interface_split/tasks/interface_split.yaml) 17 | 18 | ## interface_range 19 | 20 | The `interface_range` plugin expands an interface range and returns a list of the interfaces within that range: 21 | 22 | {{ 'Ethernet1-3' | interface_range }} returns ['Ethernet1', 'Ethernet2', 'Ethernet3'] 23 | 24 | {{ 'Ethernet1,3-4,5' | interface_range }} returns ['Ethernet1', 'Ethernet3', 'Ethernet4', 'Ethernet5'] 25 | 26 | {{ 'Ethernet1/3-5,8' | interface_range }} returns ['Ethernet1/3', 'Ethernet1/4', 'Ethernet1/5', 'Ethernet1/8'] 27 | 28 | [interface_range tests](https://github.com/ansible-network/network-engine/blob/devel/tests/interface_range/interface_range/tasks/interface_range.yaml) 29 | 30 | ## vlan_compress 31 | 32 | The `vlan_compress` plugin compresses a list of vlans into a range: 33 | 34 | {{ 'vlan1,2,3,4,5' | vlan_compress }} returns ['1-5'] 35 | 36 | {{ 'vlan1,2,4,5' | vlan_compress }} returns ['1-2,4-5'] 37 | 38 | {{ 'vlan1,2,3,5' | vlan_compress }} returns ['1-3,5'] 39 | 40 | [vlan_compress tests](https://github.com/ansible-network/network-engine/blob/devel/tests/vlan_compress/vlan_compress/tasks/vlan_compress.yaml) 41 | 42 | ## vlan_expand 43 | 44 | The `vlan_expand` plugin expands a vlan range and returns a list of the vlans within that range: 45 | 46 | {{ 'vlan1,3-5,7' | vlan_expand }} returns [1,3,4,5,7] 47 | 48 | {{ 'vlan1-5' | vlan_expand }} returns [1,2,3,4,5] 49 | 50 | [vlan_expand tests](https://github.com/ansible-network/network-engine/blob/devel/tests/vlan_expand/vlan_expand/tasks/vlan_expand.yaml) 51 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/README.md: -------------------------------------------------------------------------------- 1 | # network-engine 2 | 3 | [![network-engine Ansible Galaxy Role](https://img.shields.io/ansible/role/25206.svg)](https://galaxy.ansible.com/ansible-network/network-engine/) 4 | 5 | This role provides the foundation for building network roles by providing 6 | modules and plugins that are common to all Ansible Network roles. Typically 7 | this role should not be directly invoked in a playbook. 8 | 9 | To install this role: `ansible-galaxy install ansible-network.network-engine` 10 | 11 | To see the version of this role you currently have installed: `ansible-galaxy info ansible-network.network-engine` 12 | 13 | To ensure you have the latest version available: `ansible-galaxy install -f ansible-network.network-engine` 14 | 15 | To find other roles maintained by the Ansible Network team, see our [Galaxy Profile](https://galaxy.ansible.com/ansible-network/). 16 | 17 | Any open bugs and/or feature requests are tracked in [GitHub issues](https://github.com/ansible-network/network-engine/issues). 18 | 19 | Interested in contributing to this role? Check out [CONTRIBUTING](https://github.com/ansible-network/network-engine/blob/devel/CONTRIBUTING.md) before submitting a pull request. 20 | 21 | **Note** that this role will be renamed to `network_engine` in `2.7.0` release. 22 | 23 | ## Functions 24 | 25 | This section provides a list of the available functions that are included in 26 | this role. Any of the provided functions can be implemented in Ansible 27 | playbooks directly. To use a particular function, please see the `docs` link 28 | associated with the function. 29 | 30 | * `cli` [[source]](https://github.com/ansible-network/network-engine/blob/devel/tasks/cli.yaml) [[docs]](https://github.com/ansible-network/network-engine/blob/devel/docs/tasks/cli.md). 31 | 32 | ## Developer Guide 33 | 34 | - [How to use](https://github.com/ansible-network/network-engine/blob/devel/docs/user_guide/README.md) 35 | - [Parser Directives](https://github.com/ansible-network/network-engine/blob/devel/docs/directives/parser_directives.md) 36 | - [Filter Plugins](https://github.com/ansible-network/network-engine/blob/devel/docs/plugins/filter_plugins.md) 37 | - [How to test](https://github.com/ansible-network/network-engine/blob/devel/docs/tests/test_guide.md) 38 | 39 | 40 | ## License 41 | 42 | GPLv3 43 | 44 | ## Author Information 45 | 46 | Ansible Network Community (ansible-network) 47 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lookup_plugins/json_template.py: -------------------------------------------------------------------------------- 1 | # (c) 2018 Red Hat, Inc. 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | DOCUMENTATION = """ 10 | lookup: json_template 11 | author: Ansible Network 12 | version_added: "2.5" 13 | short_description: retrieve and template device configuration 14 | description: 15 | - This plugin lookups the content(key-value pair) of a JSON file 16 | and returns network configuration in JSON format. 17 | options: 18 | _terms: 19 | description: File for lookup 20 | """ 21 | 22 | EXAMPLES = """ 23 | - name: show interface lookup result 24 | debug: msg="{{ lookup('json_template', './show_interface.json') }} 25 | """ 26 | 27 | RETURN = """ 28 | _raw: 29 | description: JSON file content 30 | """ 31 | 32 | import json 33 | import os 34 | import sys 35 | 36 | from ansible.errors import AnsibleError, AnsibleParserError 37 | from ansible.plugins.lookup import LookupBase, display 38 | from ansible.module_utils._text import to_bytes 39 | 40 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.path.pardir, 'lib')) 41 | from network_engine.plugins import template_loader 42 | 43 | 44 | class LookupModule(LookupBase): 45 | 46 | def run(self, terms, variables, **kwargs): 47 | 48 | ret = list() 49 | 50 | self.ds = variables.copy() 51 | self.template = template_loader.get('json_template', self._templar) 52 | 53 | display.debug("File lookup term: %s" % terms[0]) 54 | 55 | lookupfile = self.find_file_in_search_path(variables, 'files', terms[0]) 56 | display.vvvv("File lookup using %s as file" % lookupfile) 57 | try: 58 | if lookupfile: 59 | with open(to_bytes(lookupfile, errors='surrogate_or_strict'), 'rb') as f: 60 | json_data = list() 61 | json_data.append(json.load(f)) 62 | ret.append(self.template.run(json_data, self.ds)) 63 | else: 64 | raise AnsibleParserError() 65 | except AnsibleParserError: 66 | raise AnsibleError("could not locate file in lookup: %s" % terms[0]) 67 | 68 | return ret 69 | -------------------------------------------------------------------------------- /roles/4500_install_image/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for 4500_install_image 3 | 4 | - name: SHOW VERSION 5 | ios_command: 6 | commands: 7 | - show version 8 | register: version 9 | 10 | - name: PARSE SHOW VERSION 11 | command_parser: 12 | file: "parsers/4500_version_parser.yml" 13 | content: "{{ version.stdout.0 }}" 14 | 15 | - name: RUN DIR 16 | ios_command: 17 | commands: 18 | - dir 19 | register: dir 20 | 21 | - name: PARSE DIR FILES 22 | command_parser: 23 | file: "parsers/4500_dir_parser.yml" 24 | content: "{{ dir.stdout.0 }}" 25 | 26 | - block: 27 | 28 | - name: CHECK IMAGE MD5 29 | ios_command: 30 | commands: 31 | - 'verify /md5 {{ dir_facts.filesystem }}{{ compliant_ios_image_name }}' 32 | register: md5 33 | 34 | - name: PARSE MD5 VALUE 35 | command_parser: 36 | file: "parsers/4500_md5_parser.yml" 37 | content: "{{ md5.stdout.0 }}" 38 | 39 | - name: CHANGE CONFIG REGISTER 40 | when: "(system.configuration_register != '0x2102')" 41 | ios_config: 42 | lines: 43 | - 'config-register 0x2102' 44 | save_when: modified 45 | 46 | - name: SET BOOT SYSTEM VARIABLE 47 | when: md5_facts.md5 == compliant_ios_md5 48 | ios_config: 49 | lines: 50 | - "no boot system" 51 | - "boot system flash {{ dir_facts.filesystem }}{{ compliant_ios_image_name }}" 52 | save_when: modified 53 | 54 | - name: REBOOT SWITCH 55 | ios_command: 56 | commands: 57 | - "reload\n" 58 | 59 | - name: WAIT FOR SWITCH TO RETURN 60 | wait_for: 61 | host: "{{inventory_hostname}}" 62 | port: 22 63 | delay: 60 64 | timeout: 600 65 | delegate_to: localhost 66 | 67 | when: system.version != compliant_ios_version 68 | 69 | - name: WAIT 2 MINUTES 70 | wait_for: 71 | host: "{{inventory_hostname}}" 72 | port: 22 73 | delay: 120 74 | timeout: 600 75 | delegate_to: localhost 76 | 77 | - name: SHOW VERSION 78 | ios_command: 79 | commands: 80 | - show version 81 | register: version 82 | 83 | - name: PARSE SHOW VERSION 84 | command_parser: 85 | file: "parsers/4500_version_parser.yml" 86 | content: "{{ version.stdout.0 }}" 87 | 88 | - debug: var=system.version 89 | 90 | - name: ASSERT THAT THE IOS VERSION IS CORRECT 91 | assert: 92 | that: 93 | - system.version == compliant_ios_version 94 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/plugins/template/__init__.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | import collections 11 | 12 | from ansible.module_utils.six import iteritems, string_types 13 | from ansible.errors import AnsibleUndefinedVariable 14 | 15 | 16 | class TemplateBase(object): 17 | 18 | def __init__(self, templar): 19 | self._templar = templar 20 | 21 | def __call__(self, data, variables, convert_bare=False): 22 | return self.template(data, variables, convert_bare) 23 | 24 | def run(self, template, variables): 25 | pass 26 | 27 | def template(self, data, variables, convert_bare=False): 28 | 29 | if isinstance(data, collections.Mapping): 30 | templated_data = {} 31 | for key, value in iteritems(data): 32 | templated_key = self.template(key, variables, convert_bare=convert_bare) 33 | templated_value = self.template(value, variables, convert_bare=convert_bare) 34 | templated_data[templated_key] = templated_value 35 | return templated_data 36 | 37 | elif isinstance(data, collections.Iterable) and not isinstance(data, string_types): 38 | return [self.template(i, variables, convert_bare=convert_bare) for i in data] 39 | 40 | else: 41 | data = data or {} 42 | tmp_avail_vars = self._templar._available_variables 43 | self._templar.set_available_variables(variables) 44 | try: 45 | resp = self._templar.template(data, convert_bare=convert_bare) 46 | except AnsibleUndefinedVariable: 47 | resp = None 48 | pass 49 | finally: 50 | self._templar.set_available_variables(tmp_avail_vars) 51 | return resp 52 | 53 | def _update(self, d, u): 54 | for k, v in iteritems(u): 55 | if isinstance(v, collections.Mapping): 56 | d[k] = self._update(d.get(k, {}), v) 57 | else: 58 | d[k] = v 59 | return d 60 | 61 | def _check_conditional(self, when, variables): 62 | conditional = "{%% if %s %%}True{%% else %%}False{%% endif %%}" 63 | return self.template(conditional % when, variables) 64 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/library/textfsm_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright 2018 Red Hat 5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 | 7 | from __future__ import absolute_import, division, print_function 8 | __metaclass__ = type 9 | 10 | 11 | ANSIBLE_METADATA = {'metadata_version': '1.1', 12 | 'status': ['preview'], 13 | 'supported_by': 'network'} 14 | 15 | DOCUMENTATION = ''' 16 | --- 17 | module: textfsm_parser 18 | author: Ansible Network Team 19 | short_description: Parses text into JSON facts using TextFSM 20 | description: 21 | - Provides textfsm rule based templates to parse data from text. 22 | The template acting as parser will iterate of the rules and parse 23 | the output of structured ASCII text into a JSON data structure 24 | that can be added to the inventory host facts. 25 | requirements: 26 | - textfsm 27 | version_added: "2.5" 28 | options: 29 | file: 30 | description: 31 | - Path to the TextFSM parser to use to parse the output from a command. 32 | The C(file) argument accepts either a relative or absolute path 33 | to the TextFSM file. 34 | default: null 35 | src: 36 | description: 37 | - The C(src) argument can be used to load the content of a TextFSM 38 | parser file. This argument allow the TextFSM parser to be loaded 39 | from an external source. See EXAMPLES. 40 | default: null 41 | content: 42 | description: 43 | - The output of the command to parse using the rules in the TextFSM 44 | file. The content should be a text string. 45 | required: true 46 | name: 47 | description: 48 | - The C(name) argument is used to define the top-level fact name to 49 | hold the output of the parser. If this argument is not provided, 50 | the output from parsing will not be exported. 51 | default: null 52 | ''' 53 | 54 | EXAMPLES = ''' 55 | - name: parse the content of a command 56 | textfsm_parser: 57 | file: files/parser_templates/show_interface.yaml 58 | content: "{{ lookup('file', 'output/show_interfaces.txt') }}" 59 | 60 | - name: store returned facts into a key call output 61 | textfsm_parser: 62 | file: files/parser_templates/show_interface.yaml 63 | content: "{{ lookup('file', 'output/show_interfaces.txt') }}" 64 | name: output 65 | 66 | - name: read the parser from an url 67 | textfsm_parser: 68 | src: "{{ lookup('url', 'http://server/path/to/parser') }}" 69 | content: "{{ lookup('file', 'output/show_interfaces.txt') }}" 70 | ''' 71 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/action_plugins/textfsm_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # (c) 2018, Ansible by Red Hat, inc 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | # 6 | # You should have received a copy of the GNU General Public License 7 | # along with Ansible. If not, see . 8 | # 9 | from __future__ import (absolute_import, division, print_function) 10 | __metaclass__ = type 11 | 12 | from ansible.module_utils.six import StringIO, string_types 13 | 14 | from ansible.plugins.action import ActionBase 15 | from ansible.errors import AnsibleError 16 | 17 | try: 18 | import textfsm 19 | HAS_TEXTFSM = True 20 | except ImportError: 21 | HAS_TEXTFSM = False 22 | 23 | 24 | class ActionModule(ActionBase): 25 | 26 | def run(self, tmp=None, task_vars=None): 27 | ''' handler for textfsm action ''' 28 | 29 | if task_vars is None: 30 | task_vars = dict() 31 | 32 | result = super(ActionModule, self).run(tmp, task_vars) 33 | del tmp # tmp no longer has any effect 34 | 35 | try: 36 | if not HAS_TEXTFSM: 37 | raise AnsibleError('textfsm_parser engine requires the TextFSM library to be installed') 38 | 39 | try: 40 | filename = self._task.args.get('file') 41 | src = self._task.args.get('src') 42 | content = self._task.args['content'] 43 | name = self._task.args.get('name') 44 | except KeyError as exc: 45 | raise AnsibleError('missing required argument: %s' % exc) 46 | 47 | if src and filename: 48 | raise AnsibleError('`src` and `file` are mutually exclusive arguments') 49 | 50 | if not isinstance(content, string_types): 51 | return {'failed': True, 'msg': '`content` must be of type str, got %s' % type(content)} 52 | 53 | if filename: 54 | tmpl = open(filename) 55 | else: 56 | tmpl = StringIO() 57 | tmpl.write(src.strip()) 58 | tmpl.seek(0) 59 | 60 | try: 61 | re_table = textfsm.TextFSM(tmpl) 62 | fsm_results = re_table.ParseText(content) 63 | 64 | except Exception as exc: 65 | raise AnsibleError(str(exc)) 66 | 67 | final_facts = [] 68 | for item in fsm_results: 69 | facts = {} 70 | facts.update(dict(zip(re_table.header, item))) 71 | final_facts.append(facts) 72 | 73 | if name: 74 | result['ansible_facts'] = {name: final_facts} 75 | else: 76 | result['ansible_facts'] = {} 77 | 78 | finally: 79 | self._remove_tmp_path(self._connection._shell.tmpdir) 80 | 81 | return result 82 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/utils.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from itertools import chain 8 | 9 | from ansible.module_utils.six import iteritems 10 | from ansible.module_utils.network.common.utils import sort_list 11 | 12 | 13 | def dict_merge(base, other): 14 | """ Return a new dict object that combines base and other 15 | 16 | This will create a new dict object that is a combination of the key/value 17 | pairs from base and other. When both keys exist, the value will be 18 | selected from other. If the value is a list object, the two lists will 19 | be combined and duplicate entries removed. 20 | 21 | :param base: dict object to serve as base 22 | :param other: dict object to combine with base 23 | 24 | :returns: new combined dict object 25 | """ 26 | if not isinstance(base, dict): 27 | raise AssertionError("`base` must be of type ") 28 | if not isinstance(other, dict): 29 | raise AssertionError("`other` must be of type ") 30 | 31 | combined = dict() 32 | 33 | for key, value in iteritems(base): 34 | if isinstance(value, dict): 35 | if key in other: 36 | item = other.get(key) 37 | if item is not None: 38 | if isinstance(other[key], dict): 39 | combined[key] = dict_merge(value, other[key]) 40 | else: 41 | combined[key] = other[key] 42 | else: 43 | combined[key] = item 44 | else: 45 | combined[key] = value 46 | elif isinstance(value, list): 47 | if key in other: 48 | item = other.get(key) 49 | if item is not None: 50 | try: 51 | combined[key] = list(set(chain(value, item))) 52 | except TypeError: 53 | value.extend([i for i in item if i not in value]) 54 | combined[key] = value 55 | else: 56 | combined[key] = item 57 | else: 58 | combined[key] = value 59 | else: 60 | if key in other: 61 | other_value = other.get(key) 62 | if other_value is not None: 63 | if sort_list(base[key]) != sort_list(other_value): 64 | combined[key] = other_value 65 | else: 66 | combined[key] = value 67 | else: 68 | combined[key] = other_value 69 | else: 70 | combined[key] = value 71 | 72 | for key in set(other.keys()).difference(base.keys()): 73 | combined[key] = other.get(key) 74 | 75 | return combined 76 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/plugins/template/json_template.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | import collections 11 | 12 | from ansible.module_utils.six import string_types 13 | 14 | from network_engine.plugins.template import TemplateBase 15 | 16 | 17 | class TemplateEngine(TemplateBase): 18 | 19 | def run(self, template, variables=None): 20 | 21 | templated_items = {} 22 | 23 | for item in template: 24 | key = self.template(item['key'], variables) 25 | 26 | # FIXME moving to the plugin system breaks this 27 | when = item.get('when') 28 | if when is not None: 29 | if not self._check_conditional(when, variables): 30 | continue 31 | 32 | if 'value' in item: 33 | value = item.get('value') 34 | items = None 35 | item_type = None 36 | 37 | elif 'object' in item: 38 | items = item.get('object') 39 | item_type = 'dict' 40 | 41 | elif 'elements' in item: 42 | items = item.get('elements') 43 | item_type = 'list' 44 | 45 | loop = item.get('repeat_for') 46 | loop_data = self.template(loop, variables) if loop else None 47 | loop_var = item.get('repeat_var', 'item') 48 | 49 | if items: 50 | if loop: 51 | if isinstance(loop_data, collections.Iterable) and not isinstance(loop_data, string_types): 52 | templated_value = list() 53 | 54 | for loop_item in loop_data: 55 | variables[loop_var] = loop_item 56 | templated_value.append(self.template(items, variables)) 57 | 58 | if item_type == 'list': 59 | templated_items[key] = templated_value 60 | 61 | elif item_type == 'dict': 62 | if key not in templated_items: 63 | templated_items[key] = {} 64 | 65 | for t in templated_value: 66 | templated_items[key] = self._update(templated_items[key], t) 67 | else: 68 | templated_items[key] = [] 69 | 70 | else: 71 | val = self.run(items, variables) 72 | 73 | if item_type == 'list': 74 | templated_value = [val] 75 | else: 76 | templated_value = val 77 | 78 | templated_items[key] = templated_value 79 | 80 | else: 81 | templated_value = self.template(value, variables) 82 | templated_items[key] = templated_value 83 | 84 | return templated_items 85 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/user_guide/textfsm_parser.md: -------------------------------------------------------------------------------- 1 | # textfsm_parser 2 | 3 | The [textfsm_parser](https://github.com/ansible-network/network-engine/blob/devel/library/textfsm_parser.py) 4 | module is based on [Google TextFSM](https://github.com/google/textfsm/wiki/TextFSM) definitions. 5 | This module iterates over matching rules defined in TextFSM format, extracts data from structured ASCII text based on those rules, 6 | and returns Ansible facts in a JSON data structure that can be added to inventory host facts and/or consumed by Ansible tasks and templates. 7 | 8 | The `textfsm_parser` module requires two inputs: 9 | - the output of commands run on the network device, passed to the `content` parameter 10 | - the parser template that defines the rules for parsing the output, passed to either the `file` or the `src` parameter 11 | 12 | ## content 13 | 14 | The `content` parameter for `textfsm_parser` must point to the ASCII text output of commands run on network devices. The text output can be in a variable or in a file. 15 | 16 | ## file 17 | 18 | The `file` parameter for `textfsm_parser` must point to a parser template that contains a TextFSM rule for each data field you want to extract from your network devices. 19 | 20 | Parser templates for the `textfsm_parser` module in the Network Engine role use TextFSM notation. 21 | 22 | ### name 23 | 24 | The `name` parameter for `textfsm_parser` names the variable in which Ansible will store the JSON data structure. If name is not set, the JSON facts from parsing will not be displayed/exported. 25 | 26 | ### src 27 | 28 | The `src` parameter for `textfsm_parser` loads your parser template from an external source, usually a URL. 29 | 30 | ## Sample Parser Templates 31 | 32 | Here is a sample TextFSM parser template: 33 | 34 | `parser_templates/ios/show_interfaces` 35 | ``` 36 | 37 | Value Required name (\S+) 38 | Value type ([\w ]+) 39 | Value description (.*) 40 | Value mtu (\d+) 41 | 42 | Start 43 | ^${name} is up 44 | ^\s+Hardware is ${type} -> Continue 45 | ^\s+Description: ${description} 46 | ^\s+MTU ${mtu} bytes, -> Record 47 | 48 | ``` 49 | 50 | ## Sample Playbooks 51 | 52 | To extract the data defined in your parser template, create a playbook that includes the Network Engine role and references the `content` and `file` parameters of the `command_parser` module. 53 | 54 | The example playbook below runs a show command, imports the Network Engine role, extracts data from the text output of the command by matching it against the rules defined 55 | in your parser template, and stores the results in a variable. To view the content of that final variable, add it to the `name` parameter as shown in the example and run the playbook in `verbose` mode: `ansible-playbook -v`. 56 | 57 | Make sure the `hosts` definition in the playbook matches a host group in your inventory - in these examples, the playbook expects a group called `ios`. 58 | 59 | The example below parses the output of the `show interfaces` command on IOS and creates facts from that output: 60 | 61 | ```yaml 62 | 63 | --- 64 | 65 | # ~/my-playbooks/textfsm-gather-interface-info.yml 66 | 67 | - hosts: ios 68 | connection: network_cli 69 | 70 | tasks: 71 | - name: Collect interface information from device 72 | ios_command: 73 | commands: "show interfaces" 74 | register: ios_interface_output 75 | 76 | - name: Generate interface facts as JSON 77 | textfsm_parser: 78 | file: "parser_templates/ios/show_interfaces" 79 | content: "{{ ios_interface_output.stdout.0 }}" 80 | name: interface_facts 81 | 82 | ``` 83 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Developer Guide 2 | 3 | This role is developed and maintained by the Ansible Network Working Group. 4 | Contributions to this role are welcomed. This document will provide individuals 5 | with information about how to contribute to the further development of this 6 | role. 7 | 8 | ## Contributing 9 | 10 | There are many ways you can contribute to this role. Adding new artifacts such 11 | as modules and plugins, testing and/or reviewing and updating documentation. 12 | 13 | ### Adding support for a new platform 14 | 15 | To add support for a new platform to this role, there are a couple of things 16 | that need to be done. 17 | 18 | 1) Create the module for the platform specific implementation in Ansible. The 19 | module can be contributed directly to Ansible core, distributed through Ansible 20 | Galaxy or added to this role. 21 | 22 | 2) (Optional) If adding the module code directly to this role, add the module 23 | to `library/` 24 | 25 | 3) (Optional) If the new platform module is distributed through another Galaxy 26 | role, please update [README](README.md) Dependencies section to include the 27 | name of the Galaxy role that includes the module. 28 | 29 | 4) Once the module has been created, the add a new task in `tasks/` for the 30 | specific platform to be supported. Use any of the existing platform 31 | implementations as a guide. 32 | 33 | 5) (Optional) If a configuration parameter is not supported, then the 34 | implementation in tasks should detect that and provide a warning message. 35 | 36 | 6) Update the `meta/main.yaml` file to add the newly provided platform to 37 | the `platforms` meta data. 38 | 39 | ### Adding platform specific arguments 40 | 41 | Sometimes there is a need to add platform specific arguments to a role for use 42 | by a platform specific module. This can be accomplished by adding the adding 43 | the arguments under a platform specific key. 44 | 45 | Note: It is the responsibility of the task writer to handle the implementation 46 | of the platform specific arguments. 47 | 48 | Here is an example that implements a platform specific argument: 49 | 50 | ```yaml 51 | tasks: 52 | - name: configure network device resource 53 | include_role: 54 | name: net_system 55 | vars: 56 | resource: 57 | foo: bar 58 | ios: 59 | foo: baz 60 | ``` 61 | 62 | ### Adding documentation for a platform specific implementation 63 | 64 | While not required, there are times when providing implementation nodes are 65 | advantageous to instructing the playbook writer how to implement platform 66 | specific arguments. In order to provide platform specific documentation, 67 | create a file in the docs directory using GitHub Markdown. The file name 68 | should be the platform name. 69 | 70 | For instance, let's assume we want to create implementation nodes for a 71 | fictitious platform call `foo`. Create a new file `docs/foo.md` and 72 | then add a link to [README](README.md) pointing to `docs/foo.md` in the `PLATFORM 73 | NOTES` section. 74 | 75 | # Note 76 | 77 | The release cadence for the network-engine role is two weeks and it will be 78 | released on every second Tuesday at 12:00 PM (GMT) from the date of prior release. 79 | For the PR to be available in the upcoming release it should be in a mergeable state 80 | that is CI is passing and all review comments fixed at least two days prior to scheduled date 81 | of release. 82 | 83 | ## Bug Reporting 84 | 85 | If you have found a bug in the with the current role please open a [GitHub 86 | issue](../../issues) 87 | 88 | ## Contact 89 | 90 | * [#ansible-network IRC channel](https://webchat.freenode.net/?channels=ansible-network) on Freenode.net 91 | 92 | -------------------------------------------------------------------------------- /parsers/4500_show_version_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show version 6 | network_os: ios 7 | 8 | - name: match softare version 9 | pattern_match: 10 | regex: ', Version (\S+)' 11 | register: version 12 | 13 | - name: match model 14 | pattern_match: 15 | regex: '^License Information for .(\S+).' 16 | register: model 17 | 18 | - name: match license level 19 | pattern_match: 20 | regex: 'License Level: (\S+)' 21 | register: license 22 | 23 | - name: match hostname 24 | pattern_match: 25 | regex: '^(\S+) uptime' 26 | register: hostname 27 | 28 | - name: match image path 29 | pattern_match: 30 | regex: '^System image file is \"(\S+)\"' 31 | register: image 32 | 33 | - name: match uptime 34 | pattern_match: 35 | regex: 'uptime is (.*)' 36 | register: uptime 37 | 38 | - name: match configuration register 39 | pattern_match: 40 | regex: 'register is (.*)' 41 | register: confreg 42 | 43 | - name: match virtual interfaces 44 | pattern_match: 45 | regex: '^(\d+) Virtual Ethernet' 46 | register: virtual_count 47 | 48 | - name: match tengig interfaces 49 | pattern_match: 50 | regex: '^(\d+) Ten' 51 | register: tengig_count 52 | 53 | - name: match os 54 | pattern_match: 55 | regex: 'Cisco IOS Software, (\S+)' 56 | register: os 57 | 58 | - name: match rom version 59 | pattern_match: 60 | regex: '^ROM: (\S+)' 61 | register: rom 62 | 63 | - name: match reload reason 64 | pattern_match: 65 | regex: '^Last reload reason: (.*)' 66 | register: reload 67 | 68 | - name: match reload time 69 | pattern_match: 70 | regex: '^System restarted at (.*)' 71 | register: reload_time 72 | 73 | - name: match license type 74 | pattern_match: 75 | regex: '^\s+License.*Type: (.*)' 76 | register: lic_type 77 | 78 | - name: match physical memory 79 | pattern_match: 80 | regex: 'with (\S+) bytes of physical memory' 81 | register: mem_bytes 82 | 83 | - name: match nvram memory 84 | pattern_match: 85 | regex: '^(\S+) bytes of non-volatile' 86 | register: nvram_bytes 87 | 88 | - name: match serial number 89 | pattern_match: 90 | regex: '^Processor board ID (\S+)' 91 | register: serial 92 | 93 | - name: build ios system state facts 94 | json_template: 95 | template: 96 | - key: version 97 | value: "{{ version.matches.0 }}" 98 | - key: hostname 99 | value: "{{ hostname.matches.0 }}" 100 | - key: image_path 101 | value: "{{ image.matches.0 }}" 102 | - key: model 103 | value: "{{ model.matches.0 }}" 104 | - key: uptime 105 | value: "{{ uptime.matches.0 }}" 106 | - key: configuration_register 107 | value: "{{ confreg.matches.0 }}" 108 | - key: license 109 | value: "{{ license.matches.0 }}" 110 | - key: virtual_count 111 | value: "{{ virtual_count.matches.0 }}" 112 | - key: tengig_count 113 | value: "{{ tengig_count.matches.0 }}" 114 | - key: os 115 | value: "{{ os.matches.0 }}" 116 | - key: rom 117 | value: "{{ rom.matches.0 }}" 118 | - key: reload_reason 119 | value: "{{ reload.matches.0 }}" 120 | - key: reload_time 121 | value: "{{ reload_time.matches.0 }}" 122 | - key: lic_type 123 | value: "{{ lic_type.matches.0 }}" 124 | - key: mem_bytes 125 | value: "{{ mem_bytes.matches.0 }}" 126 | - key: nvram_bytes 127 | value: "{{ nvram_bytes.matches.0 }}" 128 | - key: serial 129 | value: "{{ serial.matches.0 }}" 130 | register: system 131 | export: true 132 | export_as: dict 133 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/user_guide/README.md: -------------------------------------------------------------------------------- 1 | Using the Network Engine Role 2 | ---------------------------------- 3 | 4 | The Network Engine role is supported as a dependency of other Roles. The Network Engine Role extracts data about your network devices as Ansible facts in a JSON data structure, ready to be added to your inventory host facts and/or consumed by Ansible tasks and templates. You define the data elements you want to extract from each network OS command in parser templates, using either YAML or Google TextFSM syntax. The matching rules may be different on each network platform, but by defining the same variable names for the output on all platforms, you can normalize similar data across platforms. That's how the Network Engine Role supports truly platform-agnostic network automation. 5 | 6 | The Network Engine role can also be used directly, though direct usage is not supported with your Red Hat subscription. 7 | 8 | The initial release of the Network Engine role includes two parser modules: 9 | * [command_parser](https://github.com/ansible-network/network-engine/blob/devel/docs/user_guide/command_parser.md) accepts YAML input, uses an internally maintained, loosely defined parsing language based on Ansible playbook directives 10 | * [textfsm_parser](https://github.com/ansible-network/network-engine/blob/devel/docs/user_guide/textfsm_parser.md) accepts Google TextFSM input, uses Google TextFSM parsing language 11 | 12 | Both modules iterate over the data definitions in your parser templates, parse command output from your network devices (structured ASCII text) to find matches, and then convert the matches into Ansible facts in a JSON data structure. 13 | 14 | The task ```cli``` provided by the role, can also be directly implemented in your playbook. The documentation can be found here [tasks/cli](https://github.com/ansible-network/network-engine/blob/devel/docs/tasks/cli.md). 15 | 16 | To manage multiple interfaces and vlans, the Network Engine role also offers [filter_plugins](https://github.com/ansible-network/network-engine/blob/devel/docs/plugins/filter_plugins.md) that turn lists of Interfaces or VLANs into ranges and vice versa. 17 | 18 | Modules: 19 | -------- 20 | - `command_parser` 21 | - `textfsm_parser` 22 | - `net_facts` 23 | 24 | To use the Network Engine Role: 25 | ---------------------------------------- 26 | 1. Install the role from Ansible Galaxy 27 | `ansible-galaxy install ansible-network.network-engine` will copy the Network Engine role to `~/.ansible/roles/`. 28 | 1. Select the parser engine you prefer 29 | For YAML formatting, use `command_parser`; for TextFSM formatting, use `textfsm_parser`. The parser docs include 30 | examples of how to define your data and create your tasks. 31 | 1. Define the data you want to extract (or use a pre-existing parser template) 32 | See the parser_template sections of the command_parser and textfsm_parser docs for examples. 33 | 1. Create a playbook to extract the data you've defined 34 | See the Playbook sections of the command_parser and textfsm_parser docs for examples. 35 | 1. Run the playbook with `ansible-playbook -i /path/to/your/inventory -u my_user -k /path/to/your/playbook` 36 | 1. Consume the JSON-formatted Ansible facts about your device(s) in inventory, templates, and tasks. 37 | 38 | Additional Resources 39 | ------------------------------------- 40 | 41 | * [README](https://galaxy.ansible.com/ansible-network/network-engine/#readme) 42 | * [command_parser tests](https://github.com/ansible-network/network-engine/tree/devel/tests/command_parser) 43 | * [textfsm_parser tests](https://github.com/ansible-network/network-engine/tree/devel/tests/textfsm_parser) 44 | * [Full changelog diff](https://github.com/ansible-network/network-engine/blob/devel/CHANGELOG.rst) 45 | 46 | Contributing and Reporting Feedback 47 | ------------------------------------- 48 | [Review issues](https://github.com/ansible-network/network-engine/issues) 49 | -------------------------------------------------------------------------------- /roles/4500_copy_image/parsers/4500_show_version_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show version 6 | network_os: ios 7 | 8 | - name: match softare version 9 | pattern_match: 10 | regex: ', Version (\S+)' 11 | register: version 12 | 13 | - name: match model 14 | pattern_match: 15 | regex: '^License Information for .(\S+).' 16 | register: model 17 | 18 | - name: match license level 19 | pattern_match: 20 | regex: 'License Level: (\S+)' 21 | register: license 22 | 23 | - name: match hostname 24 | pattern_match: 25 | regex: '^(\S+) uptime' 26 | register: hostname 27 | 28 | - name: match image path 29 | pattern_match: 30 | regex: '^System image file is \"(\S+)\"' 31 | register: image 32 | 33 | - name: match uptime 34 | pattern_match: 35 | regex: 'uptime is (.*)' 36 | register: uptime 37 | 38 | - name: match configuration register 39 | pattern_match: 40 | regex: 'register is (.*)' 41 | register: confreg 42 | 43 | - name: match virtual interfaces 44 | pattern_match: 45 | regex: '^(\d+) Virtual Ethernet' 46 | register: virtual_count 47 | 48 | - name: match tengig interfaces 49 | pattern_match: 50 | regex: '^(\d+) Ten' 51 | register: tengig_count 52 | 53 | - name: match os 54 | pattern_match: 55 | regex: 'Cisco IOS Software, (\S+)' 56 | register: os 57 | 58 | - name: match rom version 59 | pattern_match: 60 | regex: '^ROM: (\S+)' 61 | register: rom 62 | 63 | - name: match reload reason 64 | pattern_match: 65 | regex: '^Last reload reason: (.*)' 66 | register: reload 67 | 68 | - name: match reload time 69 | pattern_match: 70 | regex: '^System restarted at (.*)' 71 | register: reload_time 72 | 73 | - name: match license type 74 | pattern_match: 75 | regex: '^\s+License.*Type: (.*)' 76 | register: lic_type 77 | 78 | - name: match physical memory 79 | pattern_match: 80 | regex: 'with (\S+) bytes of physical memory' 81 | register: mem_bytes 82 | 83 | - name: match nvram memory 84 | pattern_match: 85 | regex: '^(\S+) bytes of non-volatile' 86 | register: nvram_bytes 87 | 88 | - name: match serial number 89 | pattern_match: 90 | regex: '^Processor board ID (\S+)' 91 | register: serial 92 | 93 | - name: build ios system state facts 94 | json_template: 95 | template: 96 | - key: version 97 | value: "{{ version.matches.0 }}" 98 | - key: hostname 99 | value: "{{ hostname.matches.0 }}" 100 | - key: image_path 101 | value: "{{ image.matches.0 }}" 102 | - key: model 103 | value: "{{ model.matches.0 }}" 104 | - key: uptime 105 | value: "{{ uptime.matches.0 }}" 106 | - key: configuration_register 107 | value: "{{ confreg.matches.0 }}" 108 | - key: license 109 | value: "{{ license.matches.0 }}" 110 | - key: virtual_count 111 | value: "{{ virtual_count.matches.0 }}" 112 | - key: tengig_count 113 | value: "{{ tengig_count.matches.0 }}" 114 | - key: os 115 | value: "{{ os.matches.0 }}" 116 | - key: rom 117 | value: "{{ rom.matches.0 }}" 118 | - key: reload_reason 119 | value: "{{ reload.matches.0 }}" 120 | - key: reload_time 121 | value: "{{ reload_time.matches.0 }}" 122 | - key: lic_type 123 | value: "{{ lic_type.matches.0 }}" 124 | - key: mem_bytes 125 | value: "{{ mem_bytes.matches.0 }}" 126 | - key: nvram_bytes 127 | value: "{{ nvram_bytes.matches.0 }}" 128 | - key: serial 129 | value: "{{ serial.matches.0 }}" 130 | register: system 131 | export: true 132 | export_as: dict -------------------------------------------------------------------------------- /roles/4500_install_image/parsers/4500_show_version_parser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: parser meta data 3 | parser_metadata: 4 | version: 1.0 5 | command: show version 6 | network_os: ios 7 | 8 | - name: match softare version 9 | pattern_match: 10 | regex: ', Version (\S+)' 11 | register: version 12 | 13 | - name: match model 14 | pattern_match: 15 | regex: '^License Information for .(\S+).' 16 | register: model 17 | 18 | - name: match license level 19 | pattern_match: 20 | regex: 'License Level: (\S+)' 21 | register: license 22 | 23 | - name: match hostname 24 | pattern_match: 25 | regex: '^(\S+) uptime' 26 | register: hostname 27 | 28 | - name: match image path 29 | pattern_match: 30 | regex: '^System image file is \"(\S+)\"' 31 | register: image 32 | 33 | - name: match uptime 34 | pattern_match: 35 | regex: 'uptime is (.*)' 36 | register: uptime 37 | 38 | - name: match configuration register 39 | pattern_match: 40 | regex: 'register is (.*)' 41 | register: confreg 42 | 43 | - name: match virtual interfaces 44 | pattern_match: 45 | regex: '^(\d+) Virtual Ethernet' 46 | register: virtual_count 47 | 48 | - name: match tengig interfaces 49 | pattern_match: 50 | regex: '^(\d+) Ten' 51 | register: tengig_count 52 | 53 | - name: match os 54 | pattern_match: 55 | regex: 'Cisco IOS Software, (\S+)' 56 | register: os 57 | 58 | - name: match rom version 59 | pattern_match: 60 | regex: '^ROM: (\S+)' 61 | register: rom 62 | 63 | - name: match reload reason 64 | pattern_match: 65 | regex: '^Last reload reason: (.*)' 66 | register: reload 67 | 68 | - name: match reload time 69 | pattern_match: 70 | regex: '^System restarted at (.*)' 71 | register: reload_time 72 | 73 | - name: match license type 74 | pattern_match: 75 | regex: '^\s+License.*Type: (.*)' 76 | register: lic_type 77 | 78 | - name: match physical memory 79 | pattern_match: 80 | regex: 'with (\S+) bytes of physical memory' 81 | register: mem_bytes 82 | 83 | - name: match nvram memory 84 | pattern_match: 85 | regex: '^(\S+) bytes of non-volatile' 86 | register: nvram_bytes 87 | 88 | - name: match serial number 89 | pattern_match: 90 | regex: '^Processor board ID (\S+)' 91 | register: serial 92 | 93 | - name: build ios system state facts 94 | json_template: 95 | template: 96 | - key: version 97 | value: "{{ version.matches.0 }}" 98 | - key: hostname 99 | value: "{{ hostname.matches.0 }}" 100 | - key: image_path 101 | value: "{{ image.matches.0 }}" 102 | - key: model 103 | value: "{{ model.matches.0 }}" 104 | - key: uptime 105 | value: "{{ uptime.matches.0 }}" 106 | - key: configuration_register 107 | value: "{{ confreg.matches.0 }}" 108 | - key: license 109 | value: "{{ license.matches.0 }}" 110 | - key: virtual_count 111 | value: "{{ virtual_count.matches.0 }}" 112 | - key: tengig_count 113 | value: "{{ tengig_count.matches.0 }}" 114 | - key: os 115 | value: "{{ os.matches.0 }}" 116 | - key: rom 117 | value: "{{ rom.matches.0 }}" 118 | - key: reload_reason 119 | value: "{{ reload.matches.0 }}" 120 | - key: reload_time 121 | value: "{{ reload_time.matches.0 }}" 122 | - key: lic_type 123 | value: "{{ lic_type.matches.0 }}" 124 | - key: mem_bytes 125 | value: "{{ mem_bytes.matches.0 }}" 126 | - key: nvram_bytes 127 | value: "{{ nvram_bytes.matches.0 }}" 128 | - key: serial 129 | value: "{{ serial.matches.0 }}" 130 | register: system 131 | export: true 132 | export_as: dict -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/filter_plugins/network_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 Ansible Project 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | 4 | 5 | from __future__ import (absolute_import, division, print_function) 6 | __metaclass__ = type 7 | 8 | import re 9 | 10 | from ansible.module_utils.six import string_types 11 | from ansible.errors import AnsibleFilterError 12 | 13 | 14 | def interface_split(interface, key=None): 15 | match = re.match(r'([A-Za-z\-]*)(.+)', interface) 16 | if not match: 17 | raise AnsibleFilterError('unable to parse interface %s' % interface) 18 | obj = {'name': match.group(1), 'index': match.group(2)} 19 | if key: 20 | return obj[key] 21 | else: 22 | return obj 23 | 24 | 25 | def interface_range(interface): 26 | if not isinstance(interface, string_types): 27 | raise AnsibleFilterError('value must be of type string, got %s' % type(interface)) 28 | 29 | parts = interface.rpartition('/') 30 | if parts[1]: 31 | prefix = '%s/' % parts[0] 32 | index = parts[2] 33 | else: 34 | match = re.match(r'([A-Za-z]*)(.+)', interface) 35 | if not match: 36 | raise AnsibleFilterError('unable to parse interface %s' % interface) 37 | prefix = match.group(1) 38 | index = match.group(2) 39 | 40 | indicies = list() 41 | 42 | for item in index.split(','): 43 | tokens = item.split('-') 44 | 45 | if len(tokens) == 1: 46 | indicies.append(tokens[0]) 47 | 48 | elif len(tokens) == 2: 49 | start, end = tokens 50 | for i in range(int(start), int(end) + 1): 51 | indicies.append(i) 52 | i += 1 53 | 54 | return ['%s%s' % (prefix, index) for index in indicies] 55 | 56 | 57 | def _gen_ranges(vlan): 58 | s = e = None 59 | for i in sorted(vlan): 60 | if s is None: 61 | s = e = i 62 | elif i == e or i == e + 1: 63 | e = i 64 | else: 65 | yield (s, e) 66 | s = e = i 67 | if s is not None: 68 | yield (s, e) 69 | 70 | 71 | def vlan_compress(vlan): 72 | if not isinstance(vlan, list): 73 | raise AnsibleFilterError('value must be of type list, got %s' % type(vlan)) 74 | 75 | return (','.join(['%d' % s if s == e else '%d-%d' % (s, e) for (s, e) in _gen_ranges(vlan)])) 76 | 77 | 78 | def vlan_expand(vlan): 79 | if not isinstance(vlan, string_types): 80 | raise AnsibleFilterError('value must be of type string, got %s' % type(vlan)) 81 | 82 | match = re.match(r'([A-Za-z]*)(.+)', vlan) 83 | if not match: 84 | raise AnsibleFilterError('unable to parse vlan %s' % vlan) 85 | 86 | index = match.group(2) 87 | indices = list() 88 | 89 | for item in index.split(','): 90 | tokens = item.split('-') 91 | 92 | if len(tokens) == 1: 93 | indices.append(int(tokens[0])) 94 | 95 | elif len(tokens) == 2: 96 | start, end = tokens 97 | for i in range(int(start), int(end) + 1): 98 | indices.append(i) 99 | i += 1 100 | 101 | return ['%d' % int(index) for index in indices] 102 | 103 | 104 | def to_lines(value): 105 | if isinstance(value, (list, set, tuple)): 106 | return value 107 | elif isinstance(value, string_types): 108 | return value.split('\n') 109 | raise AnsibleFilterError('cannot convert value to lines') 110 | 111 | 112 | class FilterModule(object): 113 | ''' Network interface filter ''' 114 | 115 | def filters(self): 116 | return { 117 | 'interface_split': interface_split, 118 | 'interface_range': interface_range, 119 | 'vlan_compress': vlan_compress, 120 | 'vlan_expand': vlan_expand, 121 | 'to_lines': to_lines 122 | } 123 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/netcfg_diff/netcfg_diff/files/ios/have.txt: -------------------------------------------------------------------------------- 1 | Building configuration... 2 | 3 | 4 | Current configuration : 7250 bytes 5 | ! 6 | ! Last configuration change at 01:37:00 UTC Sun May 27 2018 by ansible 7 | ! 8 | version 14.6 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname ios01 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | ! 19 | vrf definition Mgmt-intf 20 | ! 21 | address-family ipv6 22 | exit-address-family 23 | ! 24 | address-family ipv6 25 | exit-address-family 26 | ! 27 | no logging buffered 28 | no logging console 29 | ! 30 | crypto pki trustpoint example.com 31 | enrollment selfsigned 32 | subject-name cn=samsung-ubuntu.actionmystique.net-Certificate 33 | rsakeypair example.com 34 | revocation-check none 35 | ! 36 | crypto pki certificate chain example.com 37 | username cisco privilege 15 secret 5 $1$u6jn$cZ5bnTL3wGFL3362agGH11 38 | username ansible privilege 15 secret 5 $1$R503$jIYk.0/0/toCal0O08Yr70 39 | username test 40 | ! 41 | redundancy 42 | ! 43 | no cdp run 44 | ! 45 | interface Loopback0 46 | description Loopback 47 | ip address 192.168.255.2 255.255.255.255 48 | ! 49 | interface GigabitEthernet0/0 50 | description OOB Management 51 | vrf forwarding Mgmt-intf 52 | ip address 20.20.20.20 255.255.255.0 53 | duplex full 54 | speed auto 55 | media-type rj45 56 | ! 57 | interface GigabitEthernet0/1 58 | description test-interface 59 | mtu 2000 60 | no ip address 61 | ip ospf cost 12 62 | duplex full 63 | speed 1000 64 | media-type rj45 65 | ! 66 | router ospf 1 67 | passive-interface Loopback0 68 | network 172.31.0.8 0.0.0.3 area 0 69 | network 172.31.0.12 0.0.0.3 area 0 70 | network 172.31.0.24 0.0.0.3 area 0 71 | network 192.168.255.2 0.0.0.0 area 0 72 | ! 73 | router bgp 1 74 | template peer-session IBGP 75 | remote-as 65001 76 | update-source Loopback0 77 | exit-peer-session 78 | ! 79 | bgp router-id 192.168.255.2 80 | bgp log-neighbor-changes 81 | neighbor 192.168.255.1 remote-as 1 82 | neighbor 192.168.255.1 description iBGP peer nxos01 83 | neighbor 192.168.255.1 update-source Loopback0 84 | neighbor 192.168.255.4 remote-as 1 85 | neighbor 192.168.255.4 description iBGP peer csr01 86 | neighbor 192.168.255.4 update-source Loopback0 87 | neighbor 192.168.255.5 remote-as 1 88 | neighbor 192.168.255.5 description iBGP peer iosxr01 89 | neighbor 192.168.255.5 update-source Loopback0 90 | neighbor 192.168.255.6 remote-as 1 91 | neighbor 192.168.255.6 description iBGP peer nxos02 92 | neighbor 192.168.255.6 update-source Loopback0 93 | neighbor 192.168.255.7 remote-as 1 94 | neighbor 192.168.255.7 description iBGP peer csr02 95 | neighbor 192.168.255.7 update-source Loopback0 96 | neighbor 192.168.255.8 remote-as 1 97 | neighbor 192.168.255.8 description iBGP peer ios02 98 | neighbor 192.168.255.8 update-source Loopback0 99 | neighbor 192.168.255.9 remote-as 1 100 | neighbor 192.168.255.9 description iBGP peer iosxr02 101 | neighbor 192.168.255.9 update-source Loopback0 102 | neighbor 192.168.255.10 remote-as 1 103 | neighbor 192.168.255.10 description iBGP peer nxos9k01 104 | neighbor 192.168.255.10 update-source Loopback0 105 | neighbor 192.168.255.11 remote-as 1 106 | neighbor 192.168.255.11 description iBGP peer nxos9k02 107 | neighbor 192.168.255.11 update-source Loopback0 108 | ! 109 | address-family ipv4 110 | network 192.168.255.2 mask 255.255.255.255 111 | neighbor 192.168.255.1 activate 112 | neighbor 192.168.255.4 activate 113 | neighbor 192.168.255.5 activate 114 | neighbor 192.168.255.6 activate 115 | neighbor 192.168.255.7 activate 116 | neighbor 192.168.255.8 activate 117 | neighbor 192.168.255.9 activate 118 | neighbor 192.168.255.10 activate 119 | neighbor 192.168.255.11 activate 120 | exit-address-family 121 | ! 122 | ip forward-protocol nd 123 | ! 124 | ! 125 | no ip http server 126 | no ip http secure-server 127 | ip route 192.168.10.0 255.255.255.0 192.168.1.1 2 128 | ip route vrf Mgmt-intf 10.0.0.0 255.0.0.0 10.8.38.1 129 | ip ssh version 2 130 | ip ssh pubkey-chain 131 | username ansible 132 | key-hash ssh-rsa 23C70B25FA2899AB2B77CC5719DDE5CA 133 | ip ssh server algorithm authentication publickey password 134 | ip scp server enable 135 | ! 136 | logging trap warnings 137 | ! 138 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/netcfg_diff/netcfg_diff/files/ios/want.txt: -------------------------------------------------------------------------------- 1 | Building configuration... 2 | 3 | 4 | Current configuration : 7250 bytes 5 | ! 6 | ! Last configuration change at 01:37:00 UTC Sun May 27 2018 by ansible 7 | ! 8 | version 15.6 9 | service timestamps debug datetime msec 10 | service timestamps log datetime msec 11 | no service password-encryption 12 | ! 13 | hostname ios01 14 | ! 15 | boot-start-marker 16 | boot-end-marker 17 | ! 18 | ! 19 | vrf definition Mgmt-intf 20 | ! 21 | address-family ipv4 22 | exit-address-family 23 | ! 24 | address-family ipv6 25 | exit-address-family 26 | ! 27 | no logging buffered 28 | no logging console 29 | ! 30 | crypto pki trustpoint example.com 31 | enrollment selfsigned 32 | subject-name cn=samsung-ubuntu.actionmystique.net-Certificate 33 | revocation-check none 34 | rsakeypair example.com 35 | ! 36 | crypto pki certificate chain example.com 37 | username cisco privilege 15 secret 5 $1$u6jn$cZ5bnTL3wGFL3362agGH11 38 | username ansible privilege 15 secret 5 $1$R503$jIYk.0/0/toCal0O08Yr70 39 | username test 40 | ! 41 | redundancy 42 | ! 43 | no cdp run 44 | ! 45 | interface Loopback0 46 | description Loopback 47 | ip address 192.168.255.2 255.255.255.255 48 | ! 49 | interface GigabitEthernet0/0 50 | description OOB Management 51 | vrf forwarding Mgmt-intf 52 | ip address 20.20.20.20 255.255.255.0 53 | duplex full 54 | speed auto 55 | media-type rj45 56 | ! 57 | interface GigabitEthernet0/1 58 | description test-interface 59 | mtu 2000 60 | no ip address 61 | ip ospf cost 1 62 | shutdown 63 | duplex full 64 | speed 1000 65 | media-type rj45 66 | ! 67 | router ospf 1 68 | passive-interface Loopback0 69 | network 172.31.0.8 0.0.0.3 area 0 70 | network 172.31.0.12 0.0.0.3 area 0 71 | network 172.31.0.24 0.0.0.3 area 0 72 | network 192.168.255.2 0.0.0.0 area 0 73 | ! 74 | router bgp 1 75 | template peer-session IBGP 76 | remote-as 65001 77 | update-source Loopback0 78 | exit-peer-session 79 | ! 80 | bgp router-id 192.168.255.2 81 | bgp log-neighbor-changes 82 | neighbor 192.168.255.1 remote-as 1 83 | neighbor 192.168.255.1 description iBGP peer nxos01 84 | neighbor 192.168.255.1 update-source Loopback0 85 | neighbor 192.168.255.4 remote-as 1 86 | neighbor 192.168.255.4 description iBGP peer csr01 87 | neighbor 192.168.255.4 update-source Loopback0 88 | neighbor 192.168.255.5 remote-as 1 89 | neighbor 192.168.255.5 description iBGP peer iosxr01 90 | neighbor 192.168.255.5 update-source Loopback0 91 | neighbor 192.168.255.6 remote-as 1 92 | neighbor 192.168.255.6 description iBGP peer nxos02 93 | neighbor 192.168.255.6 update-source Loopback0 94 | neighbor 192.168.255.7 remote-as 1 95 | neighbor 192.168.255.7 description iBGP peer csr02 96 | neighbor 192.168.255.7 update-source Loopback0 97 | neighbor 192.168.255.8 remote-as 1 98 | neighbor 192.168.255.8 description iBGP peer ios02 99 | neighbor 192.168.255.8 update-source Loopback0 100 | neighbor 192.168.255.9 remote-as 1 101 | neighbor 192.168.255.9 description iBGP peer iosxr02 102 | neighbor 192.168.255.9 update-source Loopback0 103 | neighbor 192.168.255.10 remote-as 1 104 | neighbor 192.168.255.10 description iBGP peer nxos9k01 105 | neighbor 192.168.255.10 update-source Loopback0 106 | neighbor 192.168.255.11 remote-as 1 107 | neighbor 192.168.255.11 description iBGP peer nxos9k02 108 | neighbor 192.168.255.11 update-source Loopback0 109 | ! 110 | address-family ipv4 111 | network 192.168.255.2 mask 255.255.255.255 112 | neighbor 192.168.255.1 activate 113 | neighbor 192.168.255.4 activate 114 | neighbor 192.168.255.5 activate 115 | neighbor 192.168.255.6 activate 116 | neighbor 192.168.255.7 activate 117 | neighbor 192.168.255.8 activate 118 | neighbor 192.168.255.9 activate 119 | neighbor 192.168.255.10 activate 120 | neighbor 192.168.255.11 activate 121 | exit-address-family 122 | ! 123 | ip forward-protocol nd 124 | ! 125 | ! 126 | no ip http server 127 | no ip http secure-server 128 | ip route 192.168.10.0 255.255.255.0 192.168.1.1 2 129 | ip route vrf Mgmt-intf 10.0.0.0 255.0.0.0 10.8.38.1 130 | ip ssh version 2 131 | ip ssh pubkey-chain 132 | username ansible 133 | key-hash ssh-rsa 23C70B25FA2899AB2B77CC5719DDE5CA 134 | ip ssh server algorithm authentication publickey password 135 | ip scp server enable 136 | ! 137 | logging trap warnings 138 | ! 139 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/action_plugins/validate_role_spec.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | ANSIBLE_METADATA = {'metadata_version': '1.1', 11 | 'status': ['preview'], 12 | 'supported_by': 'network'} 13 | 14 | DOCUMENTATION = """ 15 | --- 16 | module: validate_role_spec 17 | author: Ansible Network Team 18 | short_description: Validate required arguments are set from facts 19 | description: 20 | - This module will accept an external argument spec file that will be used to 21 | validate arguments have been configured and set properly in order to allow 22 | the role to proceed. This validate specification file provides the 23 | equivalent of the Ansible module argument spec. 24 | version_added: "2.7" 25 | options: 26 | spec: 27 | description: 28 | - Relative or absolute path to the arugment specification file to use to 29 | validate arguments are properly set for role execution. 30 | required: yes 31 | """ 32 | 33 | EXAMPLES = """ 34 | - name: use spec file for role validation 35 | validate_role_spec: 36 | spec: args.yaml 37 | """ 38 | 39 | RETURN = """ 40 | """ 41 | import os 42 | import json 43 | 44 | from ansible.plugins.action import ActionBase 45 | from ansible.module_utils._text import to_text, to_bytes 46 | from ansible.module_utils.six import iteritems, string_types 47 | from ansible.module_utils import basic 48 | from ansible.errors import AnsibleModuleError 49 | 50 | try: 51 | from __main__ import display 52 | except ImportError: 53 | from ansible.utils.display import Display 54 | display = Display() 55 | 56 | 57 | class ActionModule(ActionBase): 58 | 59 | VALID_MODULE_KWARGS = ( 60 | 'argument_spec', 'mutually_exclusive', 'required_if', 61 | 'required_one_of', 'requred_together' 62 | ) 63 | 64 | def run(self, tmp=None, task_vars=None): 65 | ''' handler for cli operations ''' 66 | 67 | if task_vars is None: 68 | task_vars = dict() 69 | 70 | result = super(ActionModule, self).run(tmp, task_vars) 71 | del tmp # tmp no longer has any effect 72 | 73 | try: 74 | spec = self._task.args['spec'] 75 | except KeyError as exc: 76 | raise AnsibleModuleError(to_text(exc)) 77 | 78 | if not spec: 79 | raise AnsibleModuleError('missing required argument: spec') 80 | 81 | spec_fp = os.path.join(task_vars['role_path'], 'meta/%s' % spec) 82 | display.vvv('using role spec %s' % spec_fp) 83 | spec = self._loader.load_from_file(spec_fp) 84 | 85 | argument_spec = spec.get('argument_spec') or {} 86 | 87 | args = {} 88 | for key, attrs in iteritems(argument_spec): 89 | if attrs is None: 90 | argument_spec[key] = {'type': 'str'} 91 | if key in task_vars: 92 | if isinstance(task_vars[key], string_types): 93 | value = self._templar.do_template(task_vars[key]) 94 | if value: 95 | args[key] = value 96 | else: 97 | args[key] = task_vars[key] 98 | elif attrs: 99 | if 'aliases' in attrs: 100 | for item in attrs['aliases']: 101 | if item in task_vars: 102 | args[key] = self._templar.do_template(task_vars[key]) 103 | elif 'default' in attrs and key not in args: 104 | args[key] = attrs['default'] 105 | 106 | basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': args})) 107 | basic.AnsibleModule.fail_json = self.fail_json 108 | 109 | spec = dict([(k, v) for k, v in iteritems(spec) if k in self.VALID_MODULE_KWARGS]) 110 | basic.AnsibleModule(**spec) 111 | 112 | self._remove_tmp_path(self._connection._shell.tmpdir) 113 | 114 | return result 115 | 116 | def fail_json(self, msg): 117 | raise AnsibleModuleError(msg) 118 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/output/ios/show_interfaces.txt: -------------------------------------------------------------------------------- 1 | GigabitEthernet0/0 is up, line protocol is up 2 | Hardware is iGbE, address is 5e00.0002.0000 (bia 5e00.0002.0000) 3 | Description: OOB Management 4 | Internet address is 10.8.38.65/24 5 | MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 6 | reliability 253/255, txload 1/255, rxload 1/255 7 | Encapsulation ARPA, loopback not set 8 | Keepalive set (10 sec) 9 | Full Duplex, Auto Speed, link type is auto, media type is RJ45 10 | output flow-control is unsupported, input flow-control is unsupported 11 | ARP type: ARPA, ARP Timeout 04:00:00 12 | Last input 00:00:00, output 00:00:00, output hang never 13 | Last clearing of "show interface" counters never 14 | Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 15 | Queueing strategy: fifo 16 | Output queue: 0/40 (size/max) 17 | 5 minute input rate 2000 bits/sec, 2 packets/sec 18 | 5 minute output rate 2000 bits/sec, 2 packets/sec 19 | 4973387 packets input, 816226566 bytes, 0 no buffer 20 | Received 228869 broadcasts (0 IP multicasts) 21 | 461509 runts, 0 giants, 0 throttles 22 | 461509 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 23 | 0 watchdog, 0 multicast, 0 pause input 24 | 3316083 packets output, 432440225 bytes, 0 underruns 25 | 0 output errors, 0 collisions, 3 interface resets 26 | 2303378 unknown protocol drops 27 | 0 babbles, 0 late collision, 0 deferred 28 | 1 lost carrier, 0 no carrier, 0 pause output 29 | 0 output buffer failures, 0 output buffers swapped out 30 | GigabitEthernet0/1 is up, line protocol is up 31 | Hardware is iGbE, address is fa16.3e4e.c5e5 (bia fa16.3e4e.c5e5) 32 | Description: test-interface 33 | MTU 2000 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 34 | reliability 255/255, txload 1/255, rxload 1/255 35 | Encapsulation ARPA, loopback not set 36 | Keepalive set (10 sec) 37 | Full Duplex, 1Gbps, link type is auto, media type is RJ45 38 | output flow-control is unsupported, input flow-control is unsupported 39 | ARP type: ARPA, ARP Timeout 04:00:00 40 | Last input 4d07h, output 4d07h, output hang never 41 | Last clearing of "show interface" counters never 42 | Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 43 | Queueing strategy: fifo 44 | Output queue: 0/40 (size/max) 45 | 5 minute input rate 0 bits/sec, 0 packets/sec 46 | 5 minute output rate 0 bits/sec, 0 packets/sec 47 | 89815 packets input, 27598643 bytes, 0 no buffer 48 | Received 1 broadcasts (0 IP multicasts) 49 | 0 runts, 0 giants, 0 throttles 50 | 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 51 | 0 watchdog, 0 multicast, 0 pause input 52 | 404016 packets output, 27896846 bytes, 0 underruns 53 | 0 output errors, 0 collisions, 51 interface resets 54 | 89728 unknown protocol drops 55 | 0 babbles, 0 late collision, 0 deferred 56 | 28 lost carrier, 0 no carrier, 0 pause output 57 | 0 output buffer failures, 0 output buffers swapped out 58 | GigabitEthernet0/2 is up, line protocol is up 59 | Hardware is iGbE, address is fa16.3eca.c938 (bia fa16.3eca.c938) 60 | Description: test-interface-2 61 | MTU 2000 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 62 | reliability 255/255, txload 1/255, rxload 1/255 63 | Encapsulation ARPA, loopback not set 64 | Keepalive set (10 sec) 65 | Full Duplex, 1Gbps, link type is auto, media type is RJ45 66 | output flow-control is unsupported, input flow-control is unsupported 67 | ARP type: ARPA, ARP Timeout 04:00:00 68 | Last input 3w1d, output 00:00:08, output hang never 69 | Last clearing of "show interface" counters never 70 | Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 71 | Queueing strategy: fifo 72 | Output queue: 0/40 (size/max) 73 | 5 minute input rate 0 bits/sec, 0 packets/sec 74 | 5 minute output rate 0 bits/sec, 0 packets/sec 75 | 487240 packets input, 36339153 bytes, 0 no buffer 76 | Received 8 broadcasts (0 IP multicasts) 77 | 183253 runts, 0 giants, 0 throttles 78 | 183253 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 79 | 0 watchdog, 0 multicast, 0 pause input 80 | 1115575 packets output, 73936479 bytes, 0 underruns 81 | 0 output errors, 0 collisions, 12 interface resets 82 | 0 unknown protocol drops 83 | 0 babbles, 0 late collision, 0 deferred 84 | 5 lost carrier, 0 no carrier, 0 pause output 85 | 0 output buffer failures, 0 output buffers swapped out 86 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/textfsm_parser/textfsm_parser/output/ios/show_interfaces.txt: -------------------------------------------------------------------------------- 1 | GigabitEthernet0/0 is up, line protocol is up 2 | Hardware is iGbE, address is 5e00.0002.0000 (bia 5e00.0002.0000) 3 | Description: OOB Management 4 | Internet address is 10.8.38.65/24 5 | MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 6 | reliability 253/255, txload 1/255, rxload 1/255 7 | Encapsulation ARPA, loopback not set 8 | Keepalive set (10 sec) 9 | Full Duplex, Auto Speed, link type is auto, media type is RJ45 10 | output flow-control is unsupported, input flow-control is unsupported 11 | ARP type: ARPA, ARP Timeout 04:00:00 12 | Last input 00:00:00, output 00:00:00, output hang never 13 | Last clearing of "show interface" counters never 14 | Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 15 | Queueing strategy: fifo 16 | Output queue: 0/40 (size/max) 17 | 5 minute input rate 2000 bits/sec, 2 packets/sec 18 | 5 minute output rate 2000 bits/sec, 2 packets/sec 19 | 4973387 packets input, 816226566 bytes, 0 no buffer 20 | Received 228869 broadcasts (0 IP multicasts) 21 | 461509 runts, 0 giants, 0 throttles 22 | 461509 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 23 | 0 watchdog, 0 multicast, 0 pause input 24 | 3316083 packets output, 432440225 bytes, 0 underruns 25 | 0 output errors, 0 collisions, 3 interface resets 26 | 2303378 unknown protocol drops 27 | 0 babbles, 0 late collision, 0 deferred 28 | 1 lost carrier, 0 no carrier, 0 pause output 29 | 0 output buffer failures, 0 output buffers swapped out 30 | GigabitEthernet0/1 is up, line protocol is up 31 | Hardware is iGbE, address is fa16.3e4e.c5e5 (bia fa16.3e4e.c5e5) 32 | Description: test-interface 33 | MTU 2000 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 34 | reliability 255/255, txload 1/255, rxload 1/255 35 | Encapsulation ARPA, loopback not set 36 | Keepalive set (10 sec) 37 | Full Duplex, 1Gbps, link type is auto, media type is RJ45 38 | output flow-control is unsupported, input flow-control is unsupported 39 | ARP type: ARPA, ARP Timeout 04:00:00 40 | Last input 4d07h, output 4d07h, output hang never 41 | Last clearing of "show interface" counters never 42 | Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 43 | Queueing strategy: fifo 44 | Output queue: 0/40 (size/max) 45 | 5 minute input rate 0 bits/sec, 0 packets/sec 46 | 5 minute output rate 0 bits/sec, 0 packets/sec 47 | 89815 packets input, 27598643 bytes, 0 no buffer 48 | Received 1 broadcasts (0 IP multicasts) 49 | 0 runts, 0 giants, 0 throttles 50 | 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 51 | 0 watchdog, 0 multicast, 0 pause input 52 | 404016 packets output, 27896846 bytes, 0 underruns 53 | 0 output errors, 0 collisions, 51 interface resets 54 | 89728 unknown protocol drops 55 | 0 babbles, 0 late collision, 0 deferred 56 | 28 lost carrier, 0 no carrier, 0 pause output 57 | 0 output buffer failures, 0 output buffers swapped out 58 | GigabitEthernet0/2 is up, line protocol is up 59 | Hardware is iGbE, address is fa16.3eca.c938 (bia fa16.3eca.c938) 60 | Description: test-interface-2 61 | MTU 2000 bytes, BW 1000000 Kbit/sec, DLY 10 usec, 62 | reliability 255/255, txload 1/255, rxload 1/255 63 | Encapsulation ARPA, loopback not set 64 | Keepalive set (10 sec) 65 | Full Duplex, 1Gbps, link type is auto, media type is RJ45 66 | output flow-control is unsupported, input flow-control is unsupported 67 | ARP type: ARPA, ARP Timeout 04:00:00 68 | Last input 3w1d, output 00:00:08, output hang never 69 | Last clearing of "show interface" counters never 70 | Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 71 | Queueing strategy: fifo 72 | Output queue: 0/40 (size/max) 73 | 5 minute input rate 0 bits/sec, 0 packets/sec 74 | 5 minute output rate 0 bits/sec, 0 packets/sec 75 | 487240 packets input, 36339153 bytes, 0 no buffer 76 | Received 8 broadcasts (0 IP multicasts) 77 | 183253 runts, 0 giants, 0 throttles 78 | 183253 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 79 | 0 watchdog, 0 multicast, 0 pause input 80 | 1115575 packets output, 73936479 bytes, 0 underruns 81 | 0 output errors, 0 collisions, 12 interface resets 82 | 0 unknown protocol drops 83 | 0 babbles, 0 late collision, 0 deferred 84 | 5 lost carrier, 0 no carrier, 0 pause output 85 | 0 output buffer failures, 0 output buffers swapped out 86 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/tests/command_parser/command_parser/tasks/ios.yaml: -------------------------------------------------------------------------------- 1 | - name: "command_parser test for {{ ansible_network_os }} show_interface" 2 | command_parser: 3 | file: "{{ parser_path }}/show_interfaces.yaml" 4 | content: "{{ lookup('file', '{{ output_path }}/show_interfaces.txt') }}" 5 | register: result 6 | vars: 7 | - ansible_network_os: ios 8 | 9 | - assert: 10 | that: 11 | - "'interface_facts' in result.ansible_facts" 12 | - "'GigabitEthernet0/0' in result.ansible_facts.interface_facts[0]" 13 | - "'GigabitEthernet0/1' in result.ansible_facts.interface_facts[1]" 14 | - "result.ansible_facts.interface_facts[0]['GigabitEthernet0/0']['config']['name'] == 'GigabitEthernet0/0'" 15 | - "result.ansible_facts.interface_facts[0]['GigabitEthernet0/0']['config']['description'] == 'OOB Management'" 16 | - "result.ansible_facts.interface_facts[1]['GigabitEthernet0/1']['config']['name'] == 'GigabitEthernet0/1'" 17 | - "result.ansible_facts.interface_facts[1]['GigabitEthernet0/1']['config']['description'] == 'test-interface'" 18 | 19 | - name: "command_parser test for {{ ansible_network_os }} show_version" 20 | command_parser: 21 | file: "{{ parser_path }}/show_version.yaml" 22 | content: "{{ lookup('file', '{{ output_path }}/show_version.txt') }}" 23 | register: result 24 | vars: 25 | - ansible_network_os: ios 26 | 27 | - assert: 28 | that: 29 | - "'system_facts' in result.ansible_facts" 30 | - "'flash0:/vios-adventerprisek9-m' in result.ansible_facts.system_facts['image_file']" 31 | - "'IOSv' in result.ansible_facts.system_facts['model']" 32 | - "'15.6(2)T' in result.ansible_facts.system_facts['version']" 33 | - "'10 weeks, 6 days, 22 hours, 30 minutes' in result.ansible_facts.system_facts['uptime']" 34 | - "'62464K' in result.ansible_facts.system_facts['memory']['free']" 35 | - "'460033K' in result.ansible_facts.system_facts['memory']['total']" 36 | 37 | - name: "command_parser expansion test for {{ ansible_network_os }} show_version" 38 | command_parser: 39 | file: "{{ parser_path }}/show_version_expand.yaml" 40 | content: "{{ lookup('file', '{{ output_path }}/show_version.txt') }}" 41 | register: result 42 | vars: 43 | - ansible_network_os: ios 44 | 45 | - assert: 46 | that: 47 | - "'system_facts' in result.ansible_facts.test.extension" 48 | - "'flash0:/vios-adventerprisek9-m' in result.ansible_facts.test.extension.system_facts['image_file']" 49 | - "'IOSv' in result.ansible_facts.test.extension.system_facts['model']" 50 | - "'15.6(2)T' in result.ansible_facts.test.extension.system_facts['version']" 51 | - "'10 weeks, 6 days, 22 hours, 30 minutes' in result.ansible_facts.test.extension.system_facts['uptime']" 52 | - "'62464K' in result.ansible_facts.test.extension.system_facts['memory']['free']" 53 | - "'460033K' in result.ansible_facts.test.extension.system_facts['memory']['total']" 54 | 55 | - name: "command_parser expansion test for {{ ansible_network_os }} show_interface" 56 | command_parser: 57 | file: "{{ parser_path }}/show_interfaces_expand.yaml" 58 | content: "{{ lookup('file', '{{ output_path }}/show_interfaces.txt') }}" 59 | register: result 60 | vars: 61 | - ansible_network_os: ios 62 | 63 | - assert: 64 | that: 65 | - "'system_facts' in result.ansible_facts.test.extension" 66 | - "'flash0:/vios-adventerprisek9-m' in result.ansible_facts.test.extension.system_facts['image_file']" 67 | - "'IOSv' in result.ansible_facts.test.extension.system_facts['model']" 68 | - "'15.6(2)T' in result.ansible_facts.test.extension.system_facts['version']" 69 | - "'10 weeks, 6 days, 22 hours, 30 minutes' in result.ansible_facts.test.extension.system_facts['uptime']" 70 | - "'62464K' in result.ansible_facts.test.extension.system_facts['memory']['free']" 71 | - "'460033K' in result.ansible_facts.test.extension.system_facts['memory']['total']" 72 | - "'interface_facts' in result.ansible_facts.test.extension" 73 | - "'GigabitEthernet0/0' in result.ansible_facts.test.extension.interface_facts[0]" 74 | - "'GigabitEthernet0/1' in result.ansible_facts.test.extension.interface_facts[1]" 75 | - "result.ansible_facts.test.extension.interface_facts[0]['GigabitEthernet0/0']['config']['name'] == 'GigabitEthernet0/0'" 76 | - "result.ansible_facts.test.extension.interface_facts[0]['GigabitEthernet0/0']['config']['description'] == 'OOB Management'" 77 | - "result.ansible_facts.test.extension.interface_facts[1]['GigabitEthernet0/1']['config']['name'] == 'GigabitEthernet0/1'" 78 | - "result.ansible_facts.test.extension.interface_facts[1]['GigabitEthernet0/1']['config']['description'] == 'test-interface'" 79 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lookup_plugins/netcfg_diff.py: -------------------------------------------------------------------------------- 1 | # (c) 2018 Red Hat, Inc. 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | DOCUMENTATION = """ 10 | lookup: netcfg_diff 11 | author: Ansible Network 12 | version_added: "2.5" 13 | short_description: Generates a text configuration difference between two configuration 14 | mainly used with network devices. 15 | description: 16 | - This plugin lookups will generate a difference between two text configuration that 17 | is supported by Network OS. This difference can be used to identify the 18 | exact change that is required to be pushed to remote host. 19 | options: 20 | _terms: 21 | description: 22 | - Specifies the wanted text configuration. Theis is usually 23 | the text configuration that is expected to be present on remote host. 24 | required: True 25 | have: 26 | description: 27 | - Specifies the text configuration. The C(have) is usually 28 | the text configuration that is active on remote host. 29 | required: True 30 | match: 31 | description: 32 | - Instructs the module on the way to perform the matching of 33 | the set of text commands between C(want) and C(have). 34 | If match is set to I(line), commands are matched line by line. If 35 | match is set to I(strict), command lines are matched with respect 36 | to position. If match is set to I(exact), command lines 37 | must be an equal match. 38 | default: line 39 | choices: ['line', 'strict', 'exact'] 40 | replace: 41 | description: 42 | - Instructs the module on the way to perform the configuration 43 | diff. If the replace argument is set to I(line) then 44 | the modified lines are pushed in the generated diff. If the replace argument 45 | is set to I(block) then the entire command block is pushed in the generated 46 | diff if any line is not correct. 47 | default: line 48 | choices: ['line', 'block'] 49 | indent: 50 | description: 51 | - Specifies the indentation used for the block in text configuration. The value of C(indent) 52 | is specific on network platform to which the text configuration in C(want) and C(have) 53 | conforms to. 54 | default: 1 55 | type: int 56 | ignore_lines: 57 | description: 58 | - This specifies the lines to be ignored while generating diff. The value of C(ignore_lines) can 59 | also be a python regex. 60 | 61 | """ 62 | 63 | EXAMPLES = """ 64 | - name: generate diff between two text configuration 65 | debug: msg="{{ lookup('netcfg_diff', want, have=have) }} 66 | """ 67 | 68 | RETURN = """ 69 | _raw: 70 | description: The text difference between values of want and have with want as base reference 71 | """ 72 | 73 | from ansible.plugins.lookup import LookupBase 74 | from ansible.module_utils.network.common.config import NetworkConfig, dumps 75 | from ansible.errors import AnsibleError 76 | 77 | 78 | MATCH_CHOICES = ('line', 'strict', 'exact') 79 | REPLACE_CHOICES = ('line', 'block') 80 | 81 | 82 | class LookupModule(LookupBase): 83 | 84 | def run(self, terms, variables, **kwargs): 85 | 86 | ret = [] 87 | 88 | try: 89 | want = terms[0] 90 | except IndexError: 91 | raise AnsibleError("value of 'want' must be specified") 92 | 93 | try: 94 | have = kwargs['have'] 95 | except KeyError: 96 | raise AnsibleError("value of 'have' must be specified") 97 | 98 | match = kwargs.get('match', 'line') 99 | if match not in MATCH_CHOICES: 100 | choices_str = ", ".join(MATCH_CHOICES) 101 | raise AnsibleError("value of match must be one of: %s, got: %s" % (choices_str, match)) 102 | 103 | replace = kwargs.get('replace', 'line') 104 | if replace not in REPLACE_CHOICES: 105 | choices_str = ", ".join(REPLACE_CHOICES) 106 | raise AnsibleError("value of replace must be one of: %s, got: %s" % (choices_str, replace)) 107 | 108 | indent = int(kwargs.get('indent', 1)) 109 | ignore_lines = kwargs.get('ignore_lines') 110 | 111 | running_obj = NetworkConfig(indent=indent, contents=have, ignore_lines=ignore_lines) 112 | candidate_obj = NetworkConfig(indent=indent, contents=want, ignore_lines=ignore_lines) 113 | 114 | configobjs = candidate_obj.difference(running_obj, match=match, replace=replace) 115 | 116 | diff = dumps(configobjs, output='commands') 117 | ret.append(diff) 118 | 119 | return ret 120 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/action_plugins/cli.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | ANSIBLE_METADATA = {'metadata_version': '1.1', 11 | 'status': ['preview'], 12 | 'supported_by': 'network'} 13 | 14 | DOCUMENTATION = """ 15 | --- 16 | module: cli 17 | author: Ansible Network Team 18 | short_description: Runs the specific command and returns the output 19 | description: 20 | - The command specified in C(command) will be executed on the remote 21 | device and its output will be returned to the module. This module 22 | requires that the device is supported using the C(network_cli) 23 | connection plugin and has a valid C(cliconf) plugin to work correctly. 24 | version_added: "2.5" 25 | options: 26 | command: 27 | description: 28 | - The command to be executed on the remote node. The value for this 29 | argument will be passed unchanged to the network device and the 30 | output returned. 31 | required: yes 32 | default: null 33 | parser: 34 | description: 35 | - The parser file to pass the output from the command through to 36 | generate Ansible facts. If this argument is specified, the output 37 | from the command will be parsed based on the rules in the 38 | specified parser. 39 | default: null 40 | engine: 41 | description: 42 | - Defines the engine to use when parsing the output. This argument 43 | accepts one of two valid values, C(command_parser) or C(textfsm_parser). 44 | default: command_parser 45 | choices: 46 | - command_parser 47 | - textfsm_parser 48 | """ 49 | 50 | EXAMPLES = """ 51 | - name: return show version 52 | cli: 53 | command: show version 54 | 55 | - name: return parsed command output 56 | cli: 57 | command: show version 58 | parser: parser_templates/show_version.yaml 59 | """ 60 | 61 | RETURN = """ 62 | stdout: 63 | description: returns the output from the command 64 | returned: always 65 | type: dict 66 | json: 67 | description: the output converted from json to a hash 68 | returned: always 69 | type: dict 70 | """ 71 | 72 | import json 73 | 74 | from ansible.plugins.action import ActionBase 75 | from ansible.module_utils.connection import Connection, ConnectionError 76 | from ansible.module_utils._text import to_text 77 | from ansible.errors import AnsibleError 78 | 79 | try: 80 | from __main__ import display 81 | except ImportError: 82 | from ansible.utils.display import Display 83 | display = Display() 84 | 85 | 86 | class ActionModule(ActionBase): 87 | 88 | def run(self, tmp=None, task_vars=None): 89 | ''' handler for cli operations ''' 90 | 91 | if task_vars is None: 92 | task_vars = dict() 93 | 94 | result = super(ActionModule, self).run(tmp, task_vars) 95 | del tmp # tmp no longer has any effect 96 | 97 | try: 98 | command = self._task.args['command'] 99 | parser = self._task.args.get('parser') 100 | engine = self._task.args.get('engine', 'command_parser') 101 | except KeyError as exc: 102 | raise AnsibleError(to_text(exc)) 103 | 104 | socket_path = getattr(self._connection, 'socket_path') or task_vars.get('ansible_socket') 105 | connection = Connection(socket_path) 106 | 107 | # make command a required argument 108 | if not command: 109 | raise AnsibleError('missing required argument `command`') 110 | 111 | try: 112 | output = connection.get(command) 113 | except ConnectionError as exc: 114 | raise AnsibleError(to_text(exc)) 115 | 116 | result['stdout'] = output 117 | 118 | # try to convert the cli output to native json 119 | try: 120 | json_data = json.loads(output) 121 | except Exception: 122 | json_data = None 123 | 124 | result['json'] = json_data 125 | 126 | if parser: 127 | if engine not in ('command_parser', 'textfsm_parser'): 128 | raise AnsibleError('missing or invalid value for argument engine') 129 | 130 | new_task = self._task.copy() 131 | new_task.args = { 132 | 'file': parser, 133 | 'content': (json_data or output) 134 | } 135 | 136 | kwargs = { 137 | 'task': new_task, 138 | 'connection': self._connection, 139 | 'play_context': self._play_context, 140 | 'loader': self._loader, 141 | 'templar': self._templar, 142 | 'shared_loader_obj': self._shared_loader_obj 143 | } 144 | 145 | task_parser = self._shared_loader_obj.action_loader.get(engine, **kwargs) 146 | result.update(task_parser.run(task_vars=task_vars)) 147 | 148 | self._remove_tmp_path(self._connection._shell.tmpdir) 149 | 150 | # this is needed so the strategy plugin can identify the connection as 151 | # a persistent connection and track it, otherwise the connection will 152 | # not be closed at the end of the play 153 | socket_path = getattr(self._connection, 'socket_path') or task_vars.get('ansible_socket') 154 | self._task.args['_ansible_socket'] = socket_path 155 | 156 | return result 157 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/lib/network_engine/plugins/parser/pattern_match.py: -------------------------------------------------------------------------------- 1 | # (c) 2018, Ansible by Red Hat, inc 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | # 4 | # You should have received a copy of the GNU General Public License 5 | # along with Ansible. If not, see . 6 | # 7 | from __future__ import (absolute_import, division, print_function) 8 | __metaclass__ = type 9 | 10 | import re 11 | 12 | from ansible.module_utils.six import iteritems 13 | 14 | 15 | def get_value(m, i): 16 | return m.group(i) if m else None 17 | 18 | 19 | class ParserEngine(object): 20 | 21 | def __init__(self, text): 22 | self.text = text 23 | 24 | def match(self, regex, match_all=None, match_until=None, match_greedy=None): 25 | """ Perform the regular expression match against the content 26 | 27 | :args regex: The regular expression pattern to use 28 | :args content: The content to run the pattern against 29 | :args match_all: Specifies if all matches of pattern should be returned 30 | or just the first occurrence 31 | 32 | :returns: list object of matches or None if there where no matches found 33 | """ 34 | content = self.text 35 | 36 | if match_greedy: 37 | return self._match_greedy(content, regex, end=match_until, match_all=match_all) 38 | elif match_all: 39 | return self._match_all(content, regex) 40 | else: 41 | return self._match(content, regex) 42 | 43 | def _match_all(self, content, pattern): 44 | match = self.re_matchall(pattern, content) 45 | if match: 46 | return match 47 | 48 | def _match(self, content, pattern): 49 | match = self.re_search(pattern, content) 50 | return match 51 | 52 | def _match_greedy(self, content, start, end=None, match_all=None): 53 | """ Filter a section of the content text for matching 54 | 55 | :args content: The content to match against 56 | :args start: The start of the section data 57 | :args end: The end of the section data 58 | :args match_all: Whether or not to match all of the instances 59 | 60 | :returns: a list object of all matches 61 | """ 62 | section_data = list() 63 | 64 | if match_all: 65 | while True: 66 | section_range = self._get_section_range(content, start, end) 67 | if not section_range: 68 | break 69 | 70 | sidx, eidx = section_range 71 | 72 | if eidx is not None: 73 | section_data.append(content[sidx: eidx]) 74 | content = content[eidx:] 75 | else: 76 | section_data.append(content[sidx:]) 77 | break 78 | 79 | else: 80 | section_data.append(content) 81 | 82 | return section_data 83 | 84 | def _get_section_range(self, content, start, end=None): 85 | 86 | context_start_re = re.compile(start, re.M) 87 | if end: 88 | context_end_re = re.compile(end, re.M) 89 | include_end = True 90 | else: 91 | context_end_re = context_start_re 92 | include_end = False 93 | 94 | context_start = re.search(context_start_re, content) 95 | if not context_start: 96 | return 97 | 98 | string_start = context_start.start() 99 | end = context_start.end() + 1 100 | 101 | context_end = re.search(context_end_re, content[end:]) 102 | if not context_end: 103 | return (string_start, None) 104 | 105 | if include_end: 106 | string_end = end + context_end.end() 107 | else: 108 | string_end = end + context_end.start() 109 | 110 | return (string_start, string_end) 111 | 112 | def _get_context_data(self, entry, content): 113 | name = entry['name'] 114 | 115 | context = entry.get('context', {}) 116 | context_data = list() 117 | 118 | if context: 119 | while True: 120 | context_range = self._get_context_range(name, context, content) 121 | 122 | if not context_range: 123 | break 124 | 125 | start, end = context_range 126 | 127 | if end is not None: 128 | context_data.append(content[start: end]) 129 | content = content[end:] 130 | else: 131 | context_data.append(content[start:]) 132 | break 133 | 134 | else: 135 | context_data.append(content) 136 | 137 | return context_data 138 | 139 | def re_search(self, regex, value): 140 | obj = {'matches': []} 141 | regex = re.compile(regex, re.M) 142 | match = regex.search(value) 143 | if match: 144 | items = list(match.groups()) 145 | if regex.groupindex: 146 | for name, index in iteritems(regex.groupindex): 147 | obj[name] = items[index - 1] 148 | obj['matches'] = items 149 | return obj 150 | 151 | def re_matchall(self, regex, value): 152 | objects = list() 153 | regex = re.compile(regex) 154 | for match in re.findall(regex.pattern, value, re.M): 155 | obj = {} 156 | obj['matches'] = match 157 | if regex.groupindex: 158 | for name, index in iteritems(regex.groupindex): 159 | if len(regex.groupindex) == 1: 160 | obj[name] = match 161 | else: 162 | obj[name] = match[index - 1] 163 | objects.append(obj) 164 | return objects 165 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/tasks/cli.md: -------------------------------------------------------------------------------- 1 | # Task cli 2 | The ```cli``` task provides an implementation for running CLI commands on 3 | network devices that is platform agnostic. The ```cli``` task accepts a 4 | command and will attempt to execute that command on the remote device returning 5 | the command ouput. 6 | 7 | If the ```parser``` argument is provided, the output from the command will be 8 | passed through the parser and returned as JSON facts using the ```engine``` 9 | argument. 10 | 11 | 12 | ## Requirements 13 | The following is the list of requirements for using the this task: 14 | 15 | * Ansible 2.5 or later 16 | * Connection ```network_cli``` 17 | * ansible_network_os 18 | 19 | ## Arguments 20 | The following are the list of required and optional arguments supported by this 21 | task. 22 | 23 | ### command 24 | This argument specifies the command to be executed on the remote device. The 25 | ```command``` argument is a required value. 26 | 27 | ### parser 28 | This argument specifies the location of the parser to pass the output from the command to 29 | in order to generate JSON data. The ```parser``` argument is an optional value, but required 30 | when ```engine``` is used. 31 | 32 | ### engine 33 | The ```engine``` argument is used to define which parsing engine to use when parsing the output 34 | of the CLI commands. This argument uses the file specified to ```parser``` for parsing output to 35 | JSON facts. This argument requires ```parser``` argument to be specified. 36 | 37 | This action currently supports two different parsers: 38 | 39 | * ```command_parser``` 40 | * ```textfsm_parser``` 41 | 42 | The default value is ```command_parser```. 43 | 44 | ## How to use 45 | This section describes how to use the ```cli``` task in a playbook. 46 | 47 | 48 | The following example runs CLI command on the network node. 49 | ```yaml 50 | 51 | --- 52 | - hosts: ios01 53 | connection: network_cli 54 | 55 | tasks: 56 | - name: run cli command with cli task 57 | import_role: 58 | name: ansible-network.network-engine 59 | tasks_from: cli 60 | vars: 61 | ansible_network_os: ios 62 | command: show version 63 | 64 | ``` 65 | 66 | When run with verbose mode, the output returned is as follows: 67 | 68 | ``` 69 | 70 | ok: [ios01] => { 71 | "changed": false, 72 | "json": null, 73 | "stdout": "Cisco IOS Software, IOSv Software (VIOS-ADVENTERPRISEK9-M), Version 15.6(2)T, RELEASE SOFTWARE (fc2)\nTechnical Support: http://www.cisco.com/techsupport\nCopyright (c) 1986-2016 by Cisco Systems, Inc.\nCompiled Tue 22-Mar-16 16:19 by prod_rel_team\n\n\nROM: Bootstrap program is IOSv\n\nan-ios-01 uptime is 19 weeks, 5 days, 19 hours, 14 minutes\nSystem returned to ROM by reload\nSystem image file is \"flash0:/vios-adventerprisek9-m\"\nLast reload reason: Unknown reason\n\n\n\nThis product contains cryptographic features and is subject to United\nStates and local country laws governing import, export, transfer and\nuse. Delivery of Cisco cryptographic products does not imply\nthird-party authority to import, export, distribute or use encryption.\nImporters, exporters, distributors and users are responsible for\ncompliance with U.S. and local country laws. By using this product you\nagree to comply with applicable laws and regulations. If you are unable\nto comply with U.S. and local laws, return this product immediately.\n\nA summary of U.S. laws governing Cisco cryptographic products may be found at:\nhttp://www.cisco.com/wwl/export/crypto/tool/stqrg.html\n\nIf you require further assistance please contact us by sending email to\nexport@cisco.com.\n\nCisco IOSv (revision 1.0) with with 460033K/62464K bytes of memory.\nProcessor board ID 92O0KON393UV5P77JRKZ5\n4 Gigabit Ethernet interfaces\nDRAM configuration is 72 bits wide with parity disabled.\n256K bytes of non-volatile configuration memory.\n2097152K bytes of ATA System CompactFlash 0 (Read/Write)\n0K bytes of ATA CompactFlash 1 (Read/Write)\n0K bytes of ATA CompactFlash 2 (Read/Write)\n10080K bytes of ATA CompactFlash 3 (Read/Write)\n\n\n\nConfiguration register is 0x0" 74 | } 75 | 76 | ``` 77 | 78 | The following example runs cli command and parse output to JSON facts. 79 | ```yaml 80 | 81 | --- 82 | - hosts: ios01 83 | connection: network_cli 84 | 85 | tasks: 86 | - name: run cli command and parse output to JSON facts 87 | import_role: 88 | name: ansible-network.network-engine 89 | tasks_from: cli 90 | vars: 91 | ansible_network_os: ios 92 | command: show version 93 | parser: parser_templates/ios/show_version.yaml 94 | engine: command_parser 95 | 96 | ``` 97 | 98 | When run with verbose mode, the output returned is as follows: 99 | 100 | ``` 101 | 102 | ok: [ios01] => { 103 | "ansible_facts": { 104 | "system_facts": { 105 | "image_file": "\"flash0:/vios-adventerprisek9-m\"", 106 | "memory": { 107 | "free": "62464K", 108 | "total": "460033K" 109 | }, 110 | "model": "IOSv", 111 | "uptime": "19 weeks, 5 days, 19 hours, 34 minutes", 112 | "version": "15.6(2)T" 113 | } 114 | }, 115 | "changed": false, 116 | "included": [ 117 | "parser_templates/ios/show_version.yaml" 118 | ], 119 | "json": null, 120 | "stdout": "Cisco IOS Software, IOSv Software (VIOS-ADVENTERPRISEK9-M), Version 15.6(2)T, RELEASE SOFTWARE (fc2)\nTechnical Support: http://www.cisco.com/techsupport\nCopyright (c) 1986-2016 by Cisco Systems, Inc.\nCompiled Tue 22-Mar-16 16:19 by prod_rel_team\n\n\nROM: Bootstrap program is IOSv\n\nan-ios-01 uptime is 19 weeks, 5 days, 19 hours, 34 minutes\nSystem returned to ROM by reload\nSystem image file is \"flash0:/vios-adventerprisek9-m\"\nLast reload reason: Unknown reason\n\n\n\nThis product contains cryptographic features and is subject to United\nStates and local country laws governing import, export, transfer and\nuse. Delivery of Cisco cryptographic products does not imply\nthird-party authority to import, export, distribute or use encryption.\nImporters, exporters, distributors and users are responsible for\ncompliance with U.S. and local country laws. By using this product you\nagree to comply with applicable laws and regulations. If you are unable\nto comply with U.S. and local laws, return this product immediately.\n\nA summary of U.S. laws governing Cisco cryptographic products may be found at:\nhttp://www.cisco.com/wwl/export/crypto/tool/stqrg.html\n\nIf you require further assistance please contact us by sending email to\nexport@cisco.com.\n\nCisco IOSv (revision 1.0) with with 460033K/62464K bytes of memory.\nProcessor board ID 92O0KON393UV5P77JRKZ5\n4 Gigabit Ethernet interfaces\nDRAM configuration is 72 bits wide with parity disabled.\n256K bytes of non-volatile configuration memory.\n2097152K bytes of ATA System CompactFlash 0 (Read/Write)\n0K bytes of ATA CompactFlash 1 (Read/Write)\n0K bytes of ATA CompactFlash 2 (Read/Write)\n10080K bytes of ATA CompactFlash 3 (Read/Write)\n\n\n\nConfiguration register is 0x0" 121 | } 122 | 123 | ``` 124 | 125 | To know how to write a parser for ```command_parser``` or ```textfsm_parser``` engine, please follow the user guide [here](https://github.com/ansible-network/network-engine/blob/devel/docs/user_guide/README.md). 126 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/user_guide/command_parser.md: -------------------------------------------------------------------------------- 1 | # command_parser 2 | 3 | The [command_parser](https://github.com/ansible-network/network-engine/blob/devel/library/command_parser.py) 4 | module is closely modeled after the Ansible playbook language. 5 | This module iterates over matching rules defined in YAML format, extracts data from structured ASCII text based on those rules, 6 | and returns Ansible facts in a JSON data structure that can be added to the inventory host facts and/or consumed by Ansible tasks and templates. 7 | 8 | The `command_parser` module requires two inputs: 9 | - the output of commands run on the network device, passed to the `content` parameter 10 | - the parser template that defines the rules for parsing the output, passed to either the `file` or the `dir` parameter 11 | 12 | ## Parameters 13 | 14 | ### content 15 | 16 | The `content` parameter for `command_parser` must point to the ASCII text output of commands run on network devices. The text output can be in a variable or in a file. 17 | 18 | 19 | ### file 20 | 21 | The `file` parameter for `command_parser` must point to a parser template that contains a rule for each data field you want to extract from your network devices. 22 | 23 | Parser templates for the `command_parser` module in the Network Engine role use YAML notation. 24 | 25 | 26 | ### dir 27 | 28 | Points to a directory containing parser templates. Use this parameter instead of `file` if your playbook uses multiple parser templates. 29 | 30 | ## Sample Parser Templates 31 | 32 | Parser templates for the `command_parser` module in the Network Engine role use YAML syntax. 33 | To write a parser template, follow the [parser_directives documentation](docs/directives/parser_directives.md). 34 | 35 | Here are two sample YAML parser templates: 36 | 37 | `parser_templates/ios/show_interfaces.yaml` 38 | ```yaml 39 | 40 | --- 41 | - name: parser meta data 42 | parser_metadata: 43 | version: 1.0 44 | command: show interface 45 | network_os: ios 46 | 47 | - name: match sections 48 | pattern_match: 49 | regex: "^(\\S+) is up," 50 | match_all: yes 51 | match_greedy: yes 52 | register: section 53 | 54 | - name: match interface values 55 | pattern_group: 56 | - name: match name 57 | pattern_match: 58 | regex: "^(\\S+)" 59 | content: "{{ item }}" 60 | register: name 61 | 62 | - name: match hardware 63 | pattern_match: 64 | regex: "Hardware is (\\S+)," 65 | content: "{{ item }}" 66 | register: type 67 | 68 | - name: match mtu 69 | pattern_match: 70 | regex: "MTU (\\d+)" 71 | content: "{{ item }}" 72 | register: mtu 73 | 74 | - name: match description 75 | pattern_match: 76 | regex: "Description: (.*)" 77 | content: "{{ item }}" 78 | register: description 79 | loop: "{{ section }}" 80 | register: interfaces 81 | 82 | - name: generate json data structure 83 | json_template: 84 | template: 85 | - key: "{{ item.name.matches.0 }}" 86 | object: 87 | - key: config 88 | object: 89 | - key: name 90 | value: "{{ item.name.matches.0 }}" 91 | - key: type 92 | value: "{{ item.type.matches.0 }}" 93 | - key: mtu 94 | value: "{{ item.mtu.matches.0 }}" 95 | - key: description 96 | value: "{{ item.description.matches.0 }}" 97 | loop: "{{ interfaces }}" 98 | export: yes 99 | register: interface_facts 100 | 101 | ``` 102 | 103 | `parser_templates/ios/show_version.yaml` 104 | 105 | ```yaml 106 | 107 | --- 108 | - name: parser meta data 109 | parser_metadata: 110 | version: 1.0 111 | command: show version 112 | network_os: ios 113 | 114 | - name: match version 115 | pattern_match: 116 | regex: "Version (\\S+)," 117 | register: version 118 | 119 | - name: match model 120 | pattern_match: 121 | regex: "^Cisco (.+) \\(revision" 122 | register: model 123 | 124 | - name: match image 125 | pattern_match: 126 | regex: "^System image file is (\\S+)" 127 | register: image 128 | 129 | - name: match uptime 130 | pattern_match: 131 | regex: "uptime is (.+)" 132 | register: uptime 133 | 134 | - name: match total memory 135 | pattern_match: 136 | regex: "with (\\S+)/(\\w*) bytes of memory" 137 | register: total_mem 138 | 139 | - name: match free memory 140 | pattern_match: 141 | regex: "with \\w*/(\\S+) bytes of memory" 142 | register: free_mem 143 | 144 | - name: export system facts to playbook 145 | set_vars: 146 | model: "{{ model.matches.0 }}" 147 | image_file: "{{ image.matches.0 }}" 148 | uptime: "{{ uptime.matches.0 }}" 149 | version: "{{ version.matches.0 }}" 150 | memory: 151 | total: "{{ total_mem.matches.0 }}" 152 | free: "{{ free_mem.matches.0 }}" 153 | export: yes 154 | register: system_facts 155 | 156 | ``` 157 | 158 | ## Sample Playbooks 159 | 160 | To extract the data defined in your parser template, create a playbook that includes the Network Engine role and references the `content` and `file` (or `dir`) parameters of the `command_parser` module. 161 | 162 | Each example playbook below runs a show command, imports the Network Engine role, extracts data from the text output of the command by matching it against the rules defined 163 | in your parser template, and stores the results in a variable. To view the content of that final variable, make sure `export: yes` is set in your parser template, and run your playbook in `verbose` mode: `ansible-playbook -vvv`. 164 | 165 | Make sure the `hosts` definition in the playbook matches a host group in your inventory - in these examples, the playbook expects a group called `ios`. 166 | 167 | The first example parses the output of the `show interfaces` command on IOS and creates facts from that output: 168 | 169 | ```yaml 170 | 171 | --- 172 | 173 | # ~/my-playbooks/gather-interface-info.yml 174 | 175 | - hosts: ios 176 | connection: network_cli 177 | 178 | tasks: 179 | - name: Collect interface information from device 180 | ios_command: 181 | commands: 182 | - show interfaces 183 | register: ios_interface_output 184 | 185 | - name: import the network-engine role 186 | import_role: 187 | name: ansible-network.network-engine 188 | 189 | - name: Generate interface facts as JSON 190 | command_parser: 191 | file: "parser_templates/ios/show_interfaces.yaml" 192 | content: "{{ ios_interface_output.stdout.0 }}" 193 | 194 | ``` 195 | 196 | The second example parses the output of the `show version` command on IOS and creates facts from that output: 197 | 198 | ```yaml 199 | 200 | --- 201 | 202 | # ~/my-playbooks/gather-version-info.yml 203 | 204 | - hosts: ios 205 | connection: network_cli 206 | 207 | tasks: 208 | - name: Collect version information from device 209 | ios_command: 210 | commands: 211 | - show version 212 | register: ios_version_output 213 | 214 | - name: import the network-engine role 215 | import_role: 216 | name: ansible-network.network-engine 217 | 218 | - name: Generate version facts as JSON 219 | command_parser: 220 | file: "parser_templates/ios/show_version.yaml" 221 | content: "{{ ios_version_output.stdout.0 }}" 222 | 223 | ``` 224 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/directives/parser_directives.md: -------------------------------------------------------------------------------- 1 | # CLI Parser Directives 2 | 3 | The `command_parser` module is a module that can be used to parse the results of 4 | text strings into Ansible facts. The primary motivation for developing the 5 | `command_parser` module is to convert structured ASCII text output (such as 6 | the stdout returned from network devices) into JSON data structures suitable to be 7 | used as host facts. 8 | 9 | The parser template file format is loosely based on the Ansible playbook directives 10 | language. It uses the Ansible directive language to ease the transition from 11 | writing playbooks to writing parser templates. However, parser templates developed using this 12 | module are not written directly into the playbook, but are a separate file 13 | called from playbooks. This is done for a variety of reasons but most notably 14 | to keep separation between the parsing logic and playbook execution. 15 | 16 | The `command_parser` works based on a set of directives that perform actions 17 | on structured data with the end result being a valid JSON structure that can be 18 | returned to the Ansible facts system. 19 | 20 | ## Parser language 21 | 22 | The parser template format uses YAML formatting, providing an ordered list of directives 23 | to be performed on the content (provided by the module argument). The overall 24 | general structure of a directive is as follows: 25 | 26 | ```yaml 27 | - name: some description name of the task to be performed 28 | directive: 29 | argument: value 30 | argument_option: value 31 | argument: value 32 | directive_option: value 33 | directive_option: value 34 | ``` 35 | 36 | The `command_parser` currently supports the following top-level directives: 37 | 38 | * `pattern_match` 39 | * `pattern_group` 40 | * `json_template` 41 | * `export_facts` 42 | 43 | In addition to the directives, the following common directive options are 44 | currently supported: 45 | 46 | * `name` 47 | * `block` 48 | * `loop` 49 | * `loop_control` 50 | 51 | * `loop_var` 52 | 53 | * `when` 54 | * `register` 55 | * `export` 56 | * `export_as` 57 | * `extend` 58 | 59 | Any of the directive options are accepted but in some cases, the option may 60 | provide no operation. For instance, when using the `export_facts` 61 | directive, the options `register`, `export` and `export_as` are all 62 | ignored. The module should provide warnings when an option is ignored. 63 | 64 | The following sections provide more details about how to use the parser 65 | directives to parse text into JSON structure. 66 | 67 | ## Directive Options 68 | 69 | This section provides details on the various options that are available to be 70 | configured on any directive. 71 | 72 | ### `name` 73 | 74 | All entries in the parser template many contain a `name` directive. The 75 | `name` directive can be used to provide an arbitrary description as to the 76 | purpose of the parser items. The use of `name` is optional for all 77 | directives. 78 | 79 | The default value for `name` is `null`. 80 | 81 | ### `register` 82 | 83 | Use the `register` option to register the results of a directive operation 84 | temporarily into the variable name you specify 85 | so you can retrieve it later in your parser template. You use `register` in 86 | a parser template just as you would in an Ansible playbook. 87 | 88 | Variables created with `register` alone are not available outside of the parser context. 89 | Any values registered are only available within the scope of the parser activities. 90 | If you want to provide values back to the playbook, you must also define the [export](#export) option. 91 | 92 | Typically you will use `register` alone for parsing each individual part of the 93 | command output, then amalgamate them into a single variable at the end of the parser template, 94 | register that variable and set `export: yes` on it. 95 | 96 | The default value for `register` is `null`. 97 | 98 | 99 | 100 | ### `export` 101 | 102 | Use the `export` option to export any value back to the calling task as an 103 | Ansible fact. The `export` option accepts a boolean value that defines if 104 | the registered fact should be exported to the calling task in the playbook (or 105 | role) scope. To export the value, simply set `export` to True. 106 | 107 | Note this option requires the `register` value to be set in some cases and will 108 | produce a warning message if the `register` option is not provided. 109 | 110 | The default value for `export` is `False`. 111 | 112 | ### `export_as` 113 | 114 | Use the `export_as` option to export a value back to the calling task as an 115 | Ansible fact in a specific format. The `export_as` option defines the structure of the exported data. 116 | Accepted values for `export_as`: 117 | 118 | * `dict` 119 | * `hash` 120 | * `object` 121 | * `list` 122 | * `elements` that defines the structure 123 | 124 | **Note** this option requires the `register` value to be set and `export: True`. 125 | Variables can also be used with `export_as`. 126 | How to use variable with `export_as` is as follows: 127 | 128 | Variable should be defined in vars or defaults or in playbook. 129 | ```yaml 130 | vars: 131 | export_type: "list" 132 | ``` 133 | 134 | Parser file needs to have the variable set to `export_as`. 135 | ``` 136 | export_as: "{{ export_type }}" 137 | ``` 138 | 139 | ### `extend` 140 | 141 | Use the `extend` option to extend a current fact hierarchy with the new 142 | registered fact. This will case the facts to be merged and returned as a 143 | single tree. If the fact doesn't previously exist, this will create the entire 144 | structure. 145 | 146 | The default value for `extend` is `null`. 147 | 148 | ### loop 149 | 150 | Use the `loop` option to loop over a directive in order to process values. 151 | With the `loop` option, the parser will iterate over the directive and 152 | provide each of the values provided by the loop content to the directive for 153 | processing. 154 | 155 | Access to the individual items is the same as it would be for Ansible 156 | playbooks. When iterating over a list of items, you can access the individual 157 | item using the `{{ item }}` variable. When looping over a hash, you can 158 | access `{{ item.key }}` and `{{ item.value }}`. 159 | 160 | ### `loop_control` 161 | 162 | Use the `loop_control` option to specify the name of the variable to be 163 | used for the loop instead of default loop variable `item`. 164 | When looping over a hash, you can access `{{ foo.key }}` and `{{ foo.value }}` where `foo` 165 | is `loop_var`. 166 | The general structure of `loop_control` is as follows: 167 | 168 | ```yaml 169 | - name: User defined variable 170 | pattern_match: 171 | regex: "^(\\S+)" 172 | content: "{{ foo }}" 173 | loop: "{{ context }}" 174 | loop_control: 175 | loop_var: foo 176 | 177 | ``` 178 | 179 | ### `when` 180 | 181 | Use the `when` option to place a condition on the directive to 182 | decided if it is executed or not. The `when` option operates the same as 183 | it would in an Ansible playbook. 184 | 185 | For example, if you only want to perform the match statement 186 | when the value of `ansible_network_os` is set to `ios`, you can apply 187 | the `when` conditional like this: 188 | 189 | ```yaml 190 | - name: conditionally matched var 191 | pattern_match: 192 | regex: "hostname (.+)" 193 | when: ansible_network_os == 'ios' 194 | ``` 195 | 196 | ## Directives 197 | 198 | The directives perform actions on the content using regular expressions to 199 | extract various values. Each directive provides some additional arguments that 200 | can be used to perform its operation. 201 | 202 | ### `pattern_match` 203 | 204 | Use the `pattern_match` directive to extract one or more values from 205 | the structured ASCII text based on regular expressions. 206 | 207 | The following arguments are supported for this directive: 208 | 209 | * `regex` 210 | * `content` 211 | * `match_all` 212 | * `match_greedy` 213 | * `match_until` : Sets a ending boundary for `match_greedy`. 214 | 215 | The `regex` argument templates the value given to it so variables and filters can be used. 216 | Example : 217 | ```yaml 218 | - name: Use a variable and a filter 219 | pattern_match: 220 | regex: "{{ inventory_hostname | lower }} (.+)" 221 | ``` 222 | 223 | ### `pattern_group` 224 | 225 | Use the `pattern_group` directive to group multiple 226 | `pattern_match` results together. 227 | 228 | The following arguments are supported for this directive: 229 | 230 | * `json_template` 231 | * `set_vars` 232 | * `export_facts` 233 | 234 | ### `json_template` 235 | 236 | Use the `json_template` directive to create a JSON data structure based on a 237 | template. This directive will allow you to template out a multi-level JSON 238 | blob. 239 | 240 | The following arguments are supported for this directive: 241 | 242 | * `template` 243 | 244 | 245 | **Note** 246 | Native jinja2 datatype (eg. 'int', 'float' etc.) rendering is supported with Ansible version >= 2.7 247 | and jinja2 library version >= 2.10. To enable native jinja2 config add below configuration in active 248 | ansible configuration file. 249 | ``` 250 | [defaults] 251 | jinja2_native= True 252 | ``` 253 | 254 | Usage example: 255 | ```yaml 256 | - set_fact: 257 | count: "1" 258 | 259 | - name: print count 260 | debug: 261 | msg: "{{ count|int }}" 262 | ``` 263 | 264 | With jinja2_native configuration enabled the output of above example task will have 265 | ``` 266 | "msg": 1 267 | ``` 268 | 269 | and with jinja2_native configuration disabled (default) output of above example task will have 270 | ``` 271 | "msg": "1" 272 | ``` 273 | 274 | ### `set_vars` 275 | 276 | Use the `set_vars` directive to set variables to the values like key / value pairs 277 | and return a dictionary. 278 | 279 | ### `export_facts` 280 | 281 | Use the `export_facts` directive to take an arbitrary set of key / value pairs 282 | and expose (return) them back to the playbook global namespace. Any key / 283 | value pairs that are provided in this directive become available on the host. 284 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/docs/directives/template_directives.md: -------------------------------------------------------------------------------- 1 | # CLI Template Directives 2 | 3 | The `network_template` module supports a number of keyword based objectives that 4 | handle how to process the template. Templates are broken up into a series 5 | of blocks that process lines. Blocks are logical groups that have a common 6 | set of properties in common. 7 | 8 | Blocks can also include other template files and are processed in the same 9 | manner as lines. See includes below for a description on how to use the 10 | include directive. 11 | 12 | The template module works by processing the lines directives in sequential 13 | order. The module will attempt to template each line in the lines directive 14 | and, if successful, add the line to the final output. Values used for 15 | variable substitution come from the host facts. If the line could not 16 | be successfully templated, the line is skipped and a warning message is 17 | displayed that the line could not be templated. 18 | 19 | There are additional directives that can be combined to support looping over 20 | lists and hashes as well as applying conditional statements to blocks, lines 21 | and includes. 22 | 23 | ## `name` 24 | 25 | Entries in the template may contain a `name` field. The `name` field 26 | is used to provide a description of the entry. It is also used to provide 27 | feedback when processing the template to indicate when an entry is 28 | skipped or fails. 29 | 30 | ## `lines` 31 | 32 | The `lines` directive provides an ordered list of statements to attempt 33 | to template. Each entry in the `lines` directive will be evaluated for 34 | variable substitution. If the entry can be successfully templated, then the 35 | output will be added to the final set of entries. If the entry cannot be 36 | successfully templated, then the entry is ignored (skipped) and a warning 37 | message is provided. If the entry in the `lines` directive contains 38 | only static text (no variables), then the line will always be processed. 39 | 40 | The `lines` directive also supports standard Jinja2 filters as well as any 41 | Ansible specific Jinja2 filters. For example, lets assume we want to add a 42 | default value if a more specific value was not assigned by a fact. 43 | 44 | ```yaml 45 | - name: render the system hostname 46 | lines: 47 | - "hostname {{ hostname | default(inventory_hostname_short }}" 48 | ``` 49 | 50 | ## `block` 51 | 52 | A group of `lines` directives can be combined into a `block` 53 | directive. These `block` directives are used to apply a common set of 54 | values to one or more `lines` or `includes` entries. 55 | 56 | For instance, a `block` directive that contains one or more `lines` 57 | entries could be use the same set of `loop` values or have a 58 | common `when` conditional statement applied to them. 59 | 60 | ## `include` 61 | 62 | Sometimes it is advantageous to break up templates into separate files and 63 | combine them. The `include` directive will instruct the current template 64 | to load another template file and process it. 65 | 66 | The `include` directive also supports variable substitution for the 67 | provided file name and can be processed with the `loop` and `when` 68 | directives. 69 | 70 | ## `when` 71 | 72 | The `when` directive allows for conditional statements to be applied to 73 | a set of `lines`, a `block` and/or the `include` directive. The 74 | `when` statement is evaluated prior to processing the statements and if 75 | the condition is true, the statements will attempt to be templated. If the 76 | statement is false, the statements are skipped and a message returned. 77 | 78 | ## `loop` 79 | 80 | Depending on the input facts, sometimes it is necessary to iterate over a 81 | set of statements. The `loop` directive allows the same set of statements 82 | to be processed in such a manner. The `loop` directive takes, as input, 83 | the name of a fact that is either a list or a hash and iterates over the 84 | statements for each entry. 85 | 86 | When the provided fact is a list of items, the value will be assigned to a 87 | variable called `item` and can be referenced by the statements. 88 | 89 | When the provided fact is a hash of items, the hash key will be assigned to 90 | the `item.key` variable and the hash value will be assigned to the 91 | `item.value` variable. Both can then be referenced by the statements. 92 | 93 | ## `loop_control` 94 | 95 | The `loop_control` directive allows the template to configure aspects 96 | related to how loops are process. This directive provides a set of suboptions 97 | to configure how loops are processed. 98 | 99 | ### `loop_var` 100 | 101 | The `loop_var` directive allows the template to override the default 102 | variable name `item`. This is useful when handling nested loops such 103 | that both inner and outer loops values can be accessed. 104 | 105 | When setting the `loop_var` to some string, the string will replace 106 | `item` as the variable name used to access the values. 107 | 108 | For example, lets assume instead of using item, we want to use a different 109 | variable name such as entry: 110 | 111 | ```yaml 112 | - name: render entries 113 | lines: 114 | - "hostname {{ entry.hostname }}" 115 | - "domain-name {{ entry.domain_name }}" 116 | loop: "{{ system }}" 117 | loop_control: 118 | loop_var: entry 119 | ``` 120 | 121 | ## `join` 122 | 123 | When building template statements that include optional values for 124 | configuration, the `join` directive can be useful. The `join` 125 | directive instructs the template to combine the templated lines together 126 | into a single string to insert into the configuration. 127 | 128 | For example, lets assume there is a need to add the following statement to 129 | the configuration: 130 | 131 | ``` 132 | ip domain-name ansible.com vrf management 133 | ip domain-name redhat.com 134 | ``` 135 | 136 | To support templating the above lines, the facts might include the domain-name 137 | and the vrf name values. Here is the example facts: 138 | 139 | ```yaml 140 | --- 141 | system: 142 | - domain_name: ansible.com 143 | vrf: management 144 | - domain_name redhat.com 145 | ``` 146 | 147 | And the template statement would be the following: 148 | 149 | ```yaml 150 | - name: render domain-name 151 | lines: 152 | - "ip domain-name {{ item.domain_name }}" 153 | - "vrf {{ item.vrf }}" 154 | loop: "{{ system }}" 155 | join: yes 156 | ``` 157 | 158 | When this entry is processed, the first iteration will successfully template 159 | both lines and add `ip domain-name ansible.com vrf management` to the 160 | output. 161 | 162 | When the second entry is processed, the first line will be successfully 163 | templated but since there is no management key, the second line will return a 164 | null value. The final line added to the configuration will be ` ip 165 | domain-name redhat.com`. 166 | 167 | If the `join` directive had been omitted, then the final set of 168 | configuration statements would be as follows: 169 | 170 | ``` 171 | ip domain-name ansible.com 172 | vrf management 173 | ip domain-name redhat.com 174 | ``` 175 | 176 | ## `join_delimiter` 177 | 178 | When the `join` delimiter is used, the templated values are combined into a 179 | single string that is added to the final output. The lines are joined using a 180 | space. The delimiting character used when processing the `join` can be 181 | modified using `join_delimiter` directive. 182 | 183 | Here is an example of using the this directive. Take the following entry: 184 | 185 | ```yaml 186 | - name: render domain-name 187 | lines: 188 | - "ip domain-name {{ item.domain_name }}" 189 | - "vrf {{ item.vrf }}" 190 | loop: "{{ system }}" 191 | join: yes 192 | join_delimiter: , 193 | ``` 194 | 195 | When the preceding statements are processed, the final output would be 196 | (assuming all variables are provided): 197 | 198 | ``` 199 | ip domain-name ansible.com,vrf management 200 | ip domain-name redhat.com 201 | ``` 202 | 203 | ## `indent` 204 | 205 | The `indent` directive is used to add one or more leading spaces to the 206 | final templated statement. It can be used to recreated a structured 207 | configuration file. 208 | 209 | Take the following template entry as an example: 210 | 211 | ```yaml 212 | - name: render the interface context 213 | lines: "interface Ethernet0/1" 214 | 215 | - name: render the interface configuration 216 | lines: 217 | - "ip address 192.168.10.1/24" 218 | - "no shutdown" 219 | - "description this is an example" 220 | indent: 3 221 | 222 | - name: render the interface context 223 | lines: "!" 224 | ``` 225 | 226 | Then the statements above are processed, the output will look like the 227 | following: 228 | 229 | ``` 230 | interface Ethernet0/1 231 | ip address 192.168.10.1/24 232 | no shutdown 233 | description this is an example 234 | ! 235 | ``` 236 | 237 | ## `required` 238 | 239 | The `required` directive specifies that all of the statements must be 240 | templated otherwise a failure is generated. Essentially it is a way to 241 | make certain that the variables are defined. 242 | 243 | For example, take the following: 244 | 245 | ```yaml 246 | - name: render router ospf context 247 | lines: 248 | - "router ospf {{ process_id }}" 249 | required: yes 250 | ``` 251 | 252 | When the above is processed, if the variable `process_id` is not present, 253 | then the statement cannot be templated. Since the `required` directive 254 | is set to true, the statement will cause the template to generate a failure 255 | message. 256 | 257 | ## `missing_key` 258 | 259 | By default, when statements are processed and a variable is undefined, the 260 | module will simply display a warning message to the screen. In some cases, it 261 | is desired to either suppress warning messages on a missing key or to force the 262 | module to fail on a missing key. 263 | 264 | To change the default behaviour, use the `missing_key` directive. This 265 | directive accepts one of three choices: 266 | 267 | * `ignore` 268 | * `warn` (default) 269 | * `fail` 270 | 271 | The value of this directive will instruct the template how to handle any 272 | condition where the desired variable is undefined. 273 | 274 | -------------------------------------------------------------------------------- /roles/ansible-network.network-engine/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Ansible Network network-engine 3 | ============================== 4 | 5 | .. _Ansible Network network-engine_v2.6.6: 6 | 7 | v2.6.6 8 | ====== 9 | 10 | .. _Ansible Network network-engine_v2.6.6_Minor Changes: 11 | 12 | Minor Changes 13 | ------------- 14 | 15 | - Capability to filter AnsibleModule kwargs `network-engine#149 `_. 16 | 17 | 18 | .. _Ansible Network network-engine_v2.6.6_Removed Features (previously deprecated): 19 | 20 | Removed Features (previously deprecated) 21 | ---------------------------------------- 22 | 23 | - Remove deprecated module ``cli_get`` 24 | 25 | 26 | .. _Ansible Network network-engine_v2.6.5: 27 | 28 | v2.6.5 29 | ====== 30 | 31 | .. _Ansible Network network-engine_v2.6.5_Bugfixes: 32 | 33 | Bugfixes 34 | -------- 35 | 36 | - Remove GenericLinux from supported platforms `network-engine#145 `_. 37 | 38 | 39 | .. _Ansible Network network-engine_v2.6.4: 40 | 41 | v2.6.4 42 | ====== 43 | 44 | .. _Ansible Network network-engine_v2.6.4_Removed Features (previously deprecated): 45 | 46 | Removed Features (previously deprecated) 47 | ---------------------------------------- 48 | 49 | - Remove deprecated module ``text_parser``. 50 | 51 | - Remove deprecated module ``textfsm``. 52 | 53 | 54 | .. _Ansible Network network-engine_v2.6.4_Bugfixes: 55 | 56 | Bugfixes 57 | -------- 58 | 59 | - Fix repeat_for in json_template `network-engine#139 `_. 60 | 61 | 62 | .. _Ansible Network network-engine_v2.6.4_Documentation Updates: 63 | 64 | Documentation Updates 65 | --------------------- 66 | 67 | - Removes unnecessary details from README `network-engine#126 `_. 68 | 69 | 70 | .. _Ansible Network network-engine_v2.6.3: 71 | 72 | v2.6.3 73 | ====== 74 | 75 | .. _Ansible Network network-engine_v2.6.3_Minor Changes: 76 | 77 | Minor Changes 78 | ------------- 79 | 80 | - Makes parser directive extend templatable `network-engine#132 `_. 81 | 82 | 83 | .. _Ansible Network network-engine_v2.6.3_Bugfixes: 84 | 85 | Bugfixes 86 | -------- 87 | 88 | - Task to fail if ansible_min_version isn't met `network-engine#130 `_. 89 | 90 | 91 | .. _Ansible Network network-engine_v2.6.2: 92 | 93 | v2.6.2 94 | ====== 95 | 96 | .. _Ansible Network network-engine_v2.6.2_New Lookup Plugins: 97 | 98 | New Lookup Plugins 99 | ------------------ 100 | 101 | - NEW ``config_template`` lookup plugin 102 | 103 | - NEW ``yang_json2xml`` lookup plugin 104 | 105 | 106 | .. _Ansible Network network-engine_v2.6.2_New Filter Plugins: 107 | 108 | New Filter Plugins 109 | ------------------ 110 | 111 | - NEW ``to_lines`` filter plugin 112 | 113 | 114 | .. _Ansible Network network-engine_v2.6.2_New Modules: 115 | 116 | New Modules 117 | ----------- 118 | 119 | - NEW ``validate_role_spec`` handle validating facts required by the role 120 | 121 | 122 | .. _Ansible Network network-engine_v2.6.2_Bugfixes: 123 | 124 | Bugfixes 125 | -------- 126 | 127 | - Fix role path test dependency `network-engine#121 `_. 128 | 129 | 130 | .. _Ansible Network network-engine_v2.6.1: 131 | 132 | v2.6.1 133 | ====== 134 | 135 | .. _Ansible Network network-engine_v2.6.1_Documentation Updates: 136 | 137 | Documentation Updates 138 | --------------------- 139 | 140 | - The argument to end a block of text when searching with match_greedy was missing `network-engine#116 `_. 141 | 142 | 143 | .. _Ansible Network network-engine_v2.6.1_Bugfixes: 144 | 145 | Bugfixes 146 | -------- 147 | 148 | - Fixes bug when loading a dir of parsers `network-engine#113 `_. 149 | 150 | 151 | .. _Ansible Network network-engine_v2.6.0: 152 | 153 | v2.6.0 154 | ====== 155 | 156 | .. _Ansible Network network-engine_v2.6.0_Major Changes: 157 | 158 | Major Changes 159 | ------------- 160 | 161 | - Initial release of 2.6.0 ``network-engine`` Ansible role that is supported with Ansible 2.6.0 162 | 163 | 164 | .. _Ansible Network network-engine_v2.5.4: 165 | 166 | v2.5.4 167 | ====== 168 | 169 | .. _Ansible Network network-engine_v2.5.4_Minor Changes: 170 | 171 | Minor Changes 172 | ------------- 173 | 174 | - Add parsers to search path `network-engine#89 `_. 175 | 176 | - Fix export_as templating vars `network-engine#104 `_. 177 | 178 | 179 | .. _Ansible Network network-engine_v2.5.4_Bugfixes: 180 | 181 | Bugfixes 182 | -------- 183 | 184 | - Fix cli task parser undefined issue when only command is used `network-engine#103 `_. 185 | 186 | - Fix an issue with using the extend directive with a loop `network-engine#105 `_. 187 | 188 | 189 | .. _Ansible Network network-engine_v2.5.3: 190 | 191 | v2.5.3 192 | ====== 193 | 194 | .. _Ansible Network network-engine_v2.5.3_Minor Changes: 195 | 196 | Minor Changes 197 | ------------- 198 | 199 | - Templating the regex sent to the parser to allow us to use ansible variables in the regex string `network-engine#97 `_. 200 | 201 | 202 | .. _Ansible Network network-engine_v2.5.3_Removed Features (previously deprecated): 203 | 204 | Removed Features (previously deprecated) 205 | ---------------------------------------- 206 | 207 | - Move yang2spec lookup to feature branch, till the right location for this plugin is identified `network-engine#100 `_. 208 | 209 | 210 | .. _Ansible Network network-engine_v2.5.2: 211 | 212 | v2.5.2 213 | ====== 214 | 215 | .. _Ansible Network network-engine_v2.5.2_Minor Changes: 216 | 217 | Minor Changes 218 | ------------- 219 | 220 | - Add new directives extend `network-engine#91 `_. 221 | 222 | - Adds conditional support to nested template objects `network-engine#55 `_. 223 | 224 | 225 | .. _Ansible Network network-engine_v2.5.2_New Lookup Plugins: 226 | 227 | New Lookup Plugins 228 | ------------------ 229 | 230 | - New lookup plugin ``json_template`` 231 | 232 | - New lookup plugin ``network_template`` 233 | 234 | - New lookup plugin ``yang2spec`` 235 | 236 | - New lookup plugin ``netcfg_diff`` 237 | 238 | 239 | .. _Ansible Network network-engine_v2.5.2_New Filter Plugins: 240 | 241 | New Filter Plugins 242 | ------------------ 243 | 244 | - New filter plugin ``interface_range`` 245 | 246 | - New filter plugin ``interface_split`` 247 | 248 | - New filter plugin ``vlan_compress`` 249 | 250 | - New filter plugin ``vlan_expand`` 251 | 252 | 253 | .. _Ansible Network network-engine_v2.5.2_New Tasks: 254 | 255 | New Tasks 256 | --------- 257 | 258 | - New task ``cli`` 259 | 260 | 261 | .. _Ansible Network network-engine_v2.5.2_Bugfixes: 262 | 263 | Bugfixes 264 | -------- 265 | 266 | - Fix AnsibleFilterError, deprecations, and unused imports `network-engine#82 `_. 267 | 268 | 269 | .. _Ansible Network network-engine_v2.5.1: 270 | 271 | v2.5.1 272 | ====== 273 | 274 | .. _Ansible Network network-engine_v2.5.1_Deprecated Features: 275 | 276 | Deprecated Features 277 | ------------------- 278 | 279 | - Module ``text_parser`` renamed to ``command_parser``; original name deprecated; legacy use supported; will be removed in 2.6.0. 280 | 281 | - Module ``textfsm`` renamed to ``textfsm_parser``; original name deprecated; legacy use supported; will be removed in 2.6.0. 282 | 283 | 284 | .. _Ansible Network network-engine_v2.5.1_New Modules: 285 | 286 | New Modules 287 | ----------- 288 | 289 | - New module ``command_parser`` (renamed from ``text_parser``) 290 | 291 | - New module ``textfsm_parser`` (renamed from ``textfsm``) 292 | 293 | 294 | .. _Ansible Network network-engine_v2.5.1_Bugfixes: 295 | 296 | Bugfixes 297 | -------- 298 | 299 | - Fix ``command_parser`` Absolute path with tilde in src should work `network-engine#58 `_ 300 | 301 | - Fix content mush only accepts string type `network-engine#72 `_ 302 | 303 | - Fix StringIO to work with Python3 in addition to Python2 `network-engine#53 `_ 304 | 305 | 306 | .. _Ansible Network network-engine_v2.5.1_Documentation Updates: 307 | 308 | Documentation Updates 309 | --------------------- 310 | 311 | - User Guide `docs/user_guide `_. 312 | 313 | 314 | .. _Ansible Network network-engine_v2.5.0: 315 | 316 | v2.5.0 317 | ====== 318 | 319 | .. _Ansible Network network-engine_v2.5.0_Major Changes: 320 | 321 | Major Changes 322 | ------------- 323 | 324 | - Initial release of the ``network-engine`` Ansible role. 325 | 326 | - This role provides the foundation for building network roles by providing modules and plugins that are common to all Ansible Network roles. All of the artifacts in this role can be used independent of the platform that is being managed. 327 | 328 | 329 | .. _Ansible Network network-engine_v2.5.0_New Modules: 330 | 331 | New Modules 332 | ----------- 333 | 334 | - NEW ``text_parser`` Parses ASCII text into JSON facts using text_parser engine and YAML-formatted input. Provides a rules-based text parser that is closely modeled after the Ansible playbook language. This parser will iterate over the rules and parse the output of structured ASCII text into a JSON data structure that can be added to the inventory host facts. 335 | 336 | - NEW ``textfsm`` Parses ASCII text into JSON facts using textfsm engine and Google TextFSM-formatted input. Provides textfsm rules-based templates to parse data from text. The template acting as parser will iterate of the rules and parse the output of structured ASCII text into a JSON data structure that can be added to the inventory host facts. 337 | 338 | --------------------------------------------------------------------------------