diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..516d62b --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,4 @@ +[settings] +known_first_party=ansible_collections.vyos.vyos +line_length=100 +profile=black diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aad517e..dc2764a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,34 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-merge-conflict - id: debug-statements - id: end-of-file-fixer - id: no-commit-to-branch - id: trailing-whitespace + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v2.7.1" + hooks: + - id: prettier + additional_dependencies: + - prettier + - prettier-plugin-toml + + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort + args: ["--filter-files"] + - repo: https://github.com/psf/black rev: 22.12.0 hooks: - id: black - repo: https://github.com/ansible-network/collection_prep rev: 1.0.1 hooks: - id: update-docs diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3b7645e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,17 @@ +# Stuff we don't want priettier to ever to look into +.*/ +coverage/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# A linked collection directory created by pytest-ansible-units +collections/ + +README.md diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index d267b5f..a10c66c 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -1,403 +1,409 @@ ancestor: null releases: 1.0.0: modules: - - description: Manage multiline banners on VyOS devices - name: vyos_banner - namespace: '' - - description: Run one or more commands on VyOS devices - name: vyos_command - namespace: '' - - description: Manage VyOS configuration on remote device - name: vyos_config - namespace: '' - - description: Get facts about vyos devices. - name: vyos_facts - namespace: '' - - description: FIREWALL global resource module - name: vyos_firewall_global - namespace: '' - - description: FIREWALL interfaces resource module - name: vyos_firewall_interfaces - namespace: '' - - description: FIREWALL rules resource module - name: vyos_firewall_rules - namespace: '' - - description: (deprecated, removed after 2022-06-01) Manage Interface on VyOS - network devices - name: vyos_interface - namespace: '' - - description: Interfaces resource module - name: vyos_interfaces - namespace: '' - - description: (deprecated, removed after 2022-06-01) Manage L3 interfaces on - VyOS network devices - name: vyos_l3_interface - namespace: '' - - description: L3 interfaces resource module - name: vyos_l3_interfaces - namespace: '' - - description: LAG interfaces resource module - name: vyos_lag_interfaces - namespace: '' - - description: (deprecated, removed after 2022-06-01) Manage link aggregation - groups on VyOS network devices - name: vyos_linkagg - namespace: '' - - description: (deprecated, removed after 2022-06-01) Manage LLDP configuration - on VyOS network devices - name: vyos_lldp - namespace: '' - - description: LLDP global resource module - name: vyos_lldp_global - namespace: '' - - description: (deprecated, removed after 2022-06-01) Manage LLDP interfaces configuration - on VyOS network devices - name: vyos_lldp_interface - namespace: '' - - description: LLDP interfaces resource module - name: vyos_lldp_interfaces - namespace: '' - - description: Manage logging on network devices - name: vyos_logging - namespace: '' - - description: OSPFv2 resource module - name: vyos_ospfv2 - namespace: '' - - description: OSPFV3 resource module - name: vyos_ospfv3 - namespace: '' - - description: Tests reachability using ping from VyOS network devices - name: vyos_ping - namespace: '' - - description: (deprecated, removed after 2022-06-01) Manage static IP routes - on Vyatta VyOS network devices - name: vyos_static_route - namespace: '' - - description: Static routes resource module - name: vyos_static_routes - namespace: '' - - description: Run `set system` commands on VyOS devices - name: vyos_system - namespace: '' - - description: Manage the collection of local users on VyOS device - name: vyos_user - namespace: '' - - description: Manage VLANs on VyOS network devices - name: vyos_vlan - namespace: '' + - description: Manage multiline banners on VyOS devices + name: vyos_banner + namespace: "" + - description: Run one or more commands on VyOS devices + name: vyos_command + namespace: "" + - description: Manage VyOS configuration on remote device + name: vyos_config + namespace: "" + - description: Get facts about vyos devices. + name: vyos_facts + namespace: "" + - description: FIREWALL global resource module + name: vyos_firewall_global + namespace: "" + - description: FIREWALL interfaces resource module + name: vyos_firewall_interfaces + namespace: "" + - description: FIREWALL rules resource module + name: vyos_firewall_rules + namespace: "" + - description: + (deprecated, removed after 2022-06-01) Manage Interface on VyOS + network devices + name: vyos_interface + namespace: "" + - description: Interfaces resource module + name: vyos_interfaces + namespace: "" + - description: + (deprecated, removed after 2022-06-01) Manage L3 interfaces on + VyOS network devices + name: vyos_l3_interface + namespace: "" + - description: L3 interfaces resource module + name: vyos_l3_interfaces + namespace: "" + - description: LAG interfaces resource module + name: vyos_lag_interfaces + namespace: "" + - description: + (deprecated, removed after 2022-06-01) Manage link aggregation + groups on VyOS network devices + name: vyos_linkagg + namespace: "" + - description: + (deprecated, removed after 2022-06-01) Manage LLDP configuration + on VyOS network devices + name: vyos_lldp + namespace: "" + - description: LLDP global resource module + name: vyos_lldp_global + namespace: "" + - description: + (deprecated, removed after 2022-06-01) Manage LLDP interfaces configuration + on VyOS network devices + name: vyos_lldp_interface + namespace: "" + - description: LLDP interfaces resource module + name: vyos_lldp_interfaces + namespace: "" + - description: Manage logging on network devices + name: vyos_logging + namespace: "" + - description: OSPFv2 resource module + name: vyos_ospfv2 + namespace: "" + - description: OSPFV3 resource module + name: vyos_ospfv3 + namespace: "" + - description: Tests reachability using ping from VyOS network devices + name: vyos_ping + namespace: "" + - description: + (deprecated, removed after 2022-06-01) Manage static IP routes + on Vyatta VyOS network devices + name: vyos_static_route + namespace: "" + - description: Static routes resource module + name: vyos_static_routes + namespace: "" + - description: Run `set system` commands on VyOS devices + name: vyos_system + namespace: "" + - description: Manage the collection of local users on VyOS device + name: vyos_user + namespace: "" + - description: Manage VLANs on VyOS network devices + name: vyos_vlan + namespace: "" plugins: cliconf: - - description: Use vyos cliconf to run command on VyOS platform - name: vyos - namespace: null - release_date: '2020-06-23' + - description: Use vyos cliconf to run command on VyOS platform + name: vyos + namespace: null + release_date: "2020-06-23" 1.0.1: changes: minor_changes: - - Add doc plugin fixes (https://github.com/ansible-collections/vyos.vyos/pull/51) + - Add doc plugin fixes (https://github.com/ansible-collections/vyos.vyos/pull/51) fragments: - - 51-doc-plugin-fixes.yaml - release_date: '2020-06-23' + - 51-doc-plugin-fixes.yaml + release_date: "2020-06-23" 1.0.2: changes: bugfixes: - - Added workaround to avoid set_fact dynamically assigning value. This behavior - seems to have been broken after ansible2.9. - - Make `src`, `backup` and `backup_options` in vyos_config work when module - alias is used (https://github.com/ansible-collections/vyos.vyos/pull/67). - - vyos_config - fixed issue where config could be saved while in check mode - (https://github.com/ansible-collections/vyos.vyos/pull/53) + - Added workaround to avoid set_fact dynamically assigning value. This behavior + seems to have been broken after ansible2.9. + - Make `src`, `backup` and `backup_options` in vyos_config work when module + alias is used (https://github.com/ansible-collections/vyos.vyos/pull/67). + - vyos_config - fixed issue where config could be saved while in check mode + (https://github.com/ansible-collections/vyos.vyos/pull/53) minor_changes: - - Fixed the typo in the modulename of ospfv2 and ospfv3 unit tests. - - Updated docs. - - terminal plugin - Added additional escape sequence to be removed from terminal - output. + - Fixed the typo in the modulename of ospfv2 and ospfv3 unit tests. + - Updated docs. + - terminal plugin - Added additional escape sequence to be removed from terminal + output. fragments: - - 65-remove-unwanted-terminal-chars.yaml - - 70-workaround-set_fact.yaml - - 72-modulename-typofix.yaml - - 73-update-docs.yaml - - fix_src_backup_with_module_alias.yaml - - prevent-vyos_config-saving-in-check-mode.yaml - release_date: '2020-07-31' + - 65-remove-unwanted-terminal-chars.yaml + - 70-workaround-set_fact.yaml + - 72-modulename-typofix.yaml + - 73-update-docs.yaml + - fix_src_backup_with_module_alias.yaml + - prevent-vyos_config-saving-in-check-mode.yaml + release_date: "2020-07-31" 1.0.3: fragments: - - 1.0.3.yaml - release_date: '2020-08-06' + - 1.0.3.yaml + release_date: "2020-08-06" 1.0.4: changes: minor_changes: - - Moved intent testcases from integration suite to unit tests. - - Reformatted files with latest version of Black (20.8b1). + - Moved intent testcases from integration suite to unit tests. + - Reformatted files with latest version of Black (20.8b1). fragments: - - 71-refactor-interface-test.yaml - - 80-reformat-files.yaml - release_date: '2020-08-27' + - 71-refactor-interface-test.yaml + - 80-reformat-files.yaml + release_date: "2020-08-27" 1.0.5: changes: bugfixes: - - Added openvpn vtu interface support. - - Update network integration auth timeout for connection local. - - terminal plugin - Overhaul ansi_re to remove more escape sequences + - Added openvpn vtu interface support. + - Update network integration auth timeout for connection local. + - terminal plugin - Overhaul ansi_re to remove more escape sequences fragments: - - 86-openvpn_vtu_interface.yaml - - local_auth_timeout.yaml - - terminal-escape-codes.yaml - release_date: '2020-10-08' + - 86-openvpn_vtu_interface.yaml + - local_auth_timeout.yaml + - terminal-escape-codes.yaml + release_date: "2020-10-08" 1.1.0: changes: minor_changes: - - Added ospf_interfaces resource module. + - Added ospf_interfaces resource module. fragments: - - vyos_ospf_interfaces_rm.yaml + - vyos_ospf_interfaces_rm.yaml modules: - - description: OSPF Interfaces Resource Module. - name: vyos_ospf_interfaces - namespace: '' - release_date: '2020-10-30' + - description: OSPF Interfaces Resource Module. + name: vyos_ospf_interfaces + namespace: "" + release_date: "2020-10-30" 1.1.1: changes: bugfixes: - - Add version key to galaxy.yaml to work around ansible-galaxy bug - - Enable configuring an interface which is not present in the running config. - - vyos_config - Only process src files as commands when they actually contain - commands. This fixes an issue were the whitespace preceding a configuration - key named 'set' was stripped, tripping up the parser. + - Add version key to galaxy.yaml to work around ansible-galaxy bug + - Enable configuring an interface which is not present in the running config. + - vyos_config - Only process src files as commands when they actually contain + commands. This fixes an issue were the whitespace preceding a configuration + key named 'set' was stripped, tripping up the parser. fragments: - - 91-new-interface.yaml - - config-processed-as-command.yaml - - galaxy-version.yaml - release_date: '2021-01-27' + - 91-new-interface.yaml + - config-processed-as-command.yaml + - galaxy-version.yaml + release_date: "2021-01-27" 2.0.0: changes: bugfixes: - - Update docs to clarify the idemptonecy related caveat and add it in the output - warnings (https://github.com/ansible-collections/ansible.netcommon/pull/189) - - cliconf plugin - Prevent `get_capabilities()` from getting larger every time - it is called + - Update docs to clarify the idemptonecy related caveat and add it in the output + warnings (https://github.com/ansible-collections/ansible.netcommon/pull/189) + - cliconf plugin - Prevent `get_capabilities()` from getting larger every time + it is called major_changes: - - Please refer to ansible.netcommon `changelog `_ - for more details. - - Requires ansible.netcommon v2.0.0+ to support `ansible_network_single_user_mode` - and `ansible_network_import_modules` - - ipaddress is no longer in ansible.netcommon. For Python versions without ipaddress - (< 3.0), the ipaddress package is now required. + - Please refer to ansible.netcommon `changelog `_ + for more details. + - Requires ansible.netcommon v2.0.0+ to support `ansible_network_single_user_mode` + and `ansible_network_import_modules` + - ipaddress is no longer in ansible.netcommon. For Python versions without ipaddress + (< 3.0), the ipaddress package is now required. minor_changes: - - Add support for configuration caching (single_user_mode). - - Add vyos BGP global resource module.(https://github.com/ansible-collections/vyos.vyos/pull/125). - - Re-use device_info dictionary in cliconf. + - Add support for configuration caching (single_user_mode). + - Add vyos BGP global resource module.(https://github.com/ansible-collections/vyos.vyos/pull/125). + - Re-use device_info dictionary in cliconf. fragments: - - 120-remove-ipaddress.yaml - - 122-rpc-unbloat.yaml - - bgp_global_resource_module.yaml - - fix_docker_sanity_test_failures.yaml - - major_release_2.0.0.yaml - - modify_resource_module_class_import.yaml - - single_user_mode.yaml - - vyos_config_diff_doc_update.yaml + - 120-remove-ipaddress.yaml + - 122-rpc-unbloat.yaml + - bgp_global_resource_module.yaml + - fix_docker_sanity_test_failures.yaml + - major_release_2.0.0.yaml + - modify_resource_module_class_import.yaml + - single_user_mode.yaml + - vyos_config_diff_doc_update.yaml modules: - - description: BGP Global Resource Module. - name: vyos_bgp_global - namespace: '' - release_date: '2021-02-24' + - description: BGP Global Resource Module. + name: vyos_bgp_global + namespace: "" + release_date: "2021-02-24" 2.1.0: changes: minor_changes: - - Add regex for delete failures to terminal_stderr_re - - Add vyos BGP address_family resource module (https://github.com/ansible-collections/vyos.vyos/pull/132). - - Enabled addition and parsing of wireguard interface. + - Add regex for delete failures to terminal_stderr_re + - Add vyos BGP address_family resource module (https://github.com/ansible-collections/vyos.vyos/pull/132). + - Enabled addition and parsing of wireguard interface. fragments: - - 128-remove_tests_sanity_requirements.yml - - 132-add-vyos-bgp-address-family.yml - - parse_wireguard_interface.yml - - replace-check_required.yaml - - skip_no_log_sanity_error.yaml - - terminal_stderr_delete.yml + - 128-remove_tests_sanity_requirements.yml + - 132-add-vyos-bgp-address-family.yml + - parse_wireguard_interface.yml + - replace-check_required.yaml + - skip_no_log_sanity_error.yaml + - terminal_stderr_delete.yml modules: - - description: BGP Address Family Resource Module. - name: vyos_bgp_address_family - namespace: '' - release_date: '2021-03-30' + - description: BGP Address Family Resource Module. + name: vyos_bgp_address_family + namespace: "" + release_date: "2021-03-30" 2.2.0: changes: minor_changes: - - Add support for available_network_resources key, which allows to fetch the - available resources for a platform (https://github.com/ansible-collections/vyos.vyos/issues/138). + - Add support for available_network_resources key, which allows to fetch the + available resources for a platform (https://github.com/ansible-collections/vyos.vyos/issues/138). security_fixes: - - Mask values of sensitive keys in module result. + - Mask values of sensitive keys in module result. fragments: - - 147-add_ignore_txt.yml - - 160_available_network_resources.yaml - - no_log_mask.yaml - - remove-old-test-deps.yml - release_date: '2021-04-27' + - 147-add_ignore_txt.yml + - 160_available_network_resources.yaml + - no_log_mask.yaml + - remove-old-test-deps.yml + release_date: "2021-04-27" 2.3.0: changes: bugfixes: - - change admin_distance to distance while generating static_routes nexthop command. - - firewall_global - port-groups were not added (https://github.com/ansible-collections/vyos.vyos/issues/107) + - change admin_distance to distance while generating static_routes nexthop command. + - firewall_global - port-groups were not added (https://github.com/ansible-collections/vyos.vyos/issues/107) minor_changes: - - Add vyos_route_maps resource module (https://github.com/ansible-collections/vyos.vyos/pull/156.). + - Add vyos_route_maps resource module (https://github.com/ansible-collections/vyos.vyos/pull/156.). fragments: - - changelog_doc_path_update.yaml - - fix_delete_route_maps.yaml - - fix_port_groups.yaml - - fix_static_routes_distance.yaml - - vyos-route-maps.yaml + - changelog_doc_path_update.yaml + - fix_delete_route_maps.yaml + - fix_port_groups.yaml + - fix_static_routes_distance.yaml + - vyos-route-maps.yaml modules: - - description: Route Map Resource Module. - name: vyos_route_maps - namespace: '' - release_date: '2021-05-18' + - description: Route Map Resource Module. + name: vyos_route_maps + namespace: "" + release_date: "2021-05-18" 2.3.1: changes: bugfixes: - - Fix KeyError 'source' - vyos_firewall_rules - - Updated docs resolving spelling typos - - change interface to next-hop-interface while generating static_routes nexthop - command. + - Fix KeyError 'source' - vyos_firewall_rules + - Updated docs resolving spelling typos + - change interface to next-hop-interface while generating static_routes nexthop + command. fragments: - - 172-fix-source-keyerror-firewallrules.yml - - docs_common_error_correction.yaml - - fix_static_routes_next_hop_interface.yaml - - update_readme_freenode_to_liberachat.yml - release_date: '2021-06-22' + - 172-fix-source-keyerror-firewallrules.yml + - docs_common_error_correction.yaml + - fix_static_routes_next_hop_interface.yaml + - update_readme_freenode_to_liberachat.yml + release_date: "2021-06-22" 2.4.0: changes: minor_changes: - - Add vyos_prefix_lists Resource Module. + - Add vyos_prefix_lists Resource Module. fragments: - - vyos_prefix_lists.yml + - vyos_prefix_lists.yml modules: - - description: Prefix-Lists resource module for VyOS - name: vyos_prefix_lists - namespace: '' - release_date: '2021-07-06' + - description: Prefix-Lists resource module for VyOS + name: vyos_prefix_lists + namespace: "" + release_date: "2021-07-06" 2.5.0: changes: bugfixes: - - fix issue in route-maps facts code when route-maps facts are empty. + - fix issue in route-maps facts code when route-maps facts are empty. deprecated_features: - - The vyos_logging module has been deprecated in favor of the new vyos_logging_global - resource module and will be removed in a release after "2023-08-01". + - The vyos_logging module has been deprecated in favor of the new vyos_logging_global + resource module and will be removed in a release after "2023-08-01". minor_changes: - - vyos_logging_global logging resource module. + - vyos_logging_global logging resource module. fragments: - - fix_issue_vyos_facts.yaml - - vyos_logging_global_module.yaml - release_date: '2021-07-26' + - fix_issue_vyos_facts.yaml + - vyos_logging_global_module.yaml + release_date: "2021-07-26" 2.5.1: changes: bugfixes: - - fix issue in firewall rules facts code when IPV6 ICMP type name in vyos.vyos.vyos_firewall_rules - is not idempotent + - fix issue in firewall rules facts code when IPV6 ICMP type name in vyos.vyos.vyos_firewall_rules + is not idempotent fragments: - - doc_updated_correct_platform.yaml - - fix_issue170_vyos_firewall_rules.yaml - - fix_pylint_issues.yaml - - tests_become.yaml - release_date: '2021-08-27' + - doc_updated_correct_platform.yaml + - fix_issue170_vyos_firewall_rules.yaml + - fix_pylint_issues.yaml + - tests_become.yaml + release_date: "2021-08-27" 2.6.0: changes: bugfixes: - - Fix vyos_firewall_rules with state replaced to only replace the specified - rules. + - Fix vyos_firewall_rules with state replaced to only replace the specified + rules. minor_changes: - - Add vyos_ntp Resource Module - - Adds support for specifying an `afi` for an `address_group` for `vyos.vyos.firewall_global`. As - a result, `address_group` now supports IPv6. - - Adds support for specifying an `afi` for an `network_group` for `vyos.vyos.firewall_global`. As - a result, `network_group` now supports IPv6. + - Add vyos_ntp Resource Module + - Adds support for specifying an `afi` for an `address_group` for `vyos.vyos.firewall_global`. As + a result, `address_group` now supports IPv6. + - Adds support for specifying an `afi` for an `network_group` for `vyos.vyos.firewall_global`. As + a result, `network_group` now supports IPv6. fragments: - - 0-copy_ignore_txt.yml - - fix-firewall_rules-state-replaced.yaml - - set_allow_duplicates.yaml - - support_ipv6_address_and_network_groups.yaml - - vyos_ntp.yml - release_date: '2021-10-02' + - 0-copy_ignore_txt.yml + - fix-firewall_rules-state-replaced.yaml + - set_allow_duplicates.yaml + - support_ipv6_address_and_network_groups.yaml + - vyos_ntp.yml + release_date: "2021-10-02" 2.7.0: changes: major_changes: - - Add 'pool' as value to server key in ntp_global. + - Add 'pool' as value to server key in ntp_global. minor_changes: - - Add vyos_snmp_server resource module. + - Add vyos_snmp_server resource module. fragments: - - add_snmp_server_resource_module.yaml - - ntp_server_add_pool.yaml - - remove-swap-file.yml - - sanity_issue_fix.yaml + - add_snmp_server_resource_module.yaml + - ntp_server_add_pool.yaml + - remove-swap-file.yml + - sanity_issue_fix.yaml modules: - - description: Manages snmp_server resource module - name: vyos_snmp_server - namespace: '' - release_date: '2022-01-31' + - description: Manages snmp_server resource module + name: vyos_snmp_server + namespace: "" + release_date: "2022-01-31" 2.8.0: changes: minor_changes: - - Add vyos_hostname resource module. - - Rename V4-EGRESS/V6-EGRESS to EGRESS in the tests to test the same-name situation - - Update vyos_facts to support IPv4 and IPv6 rule sets having the same name - - Update vyos_firewall_rules to support IPv4 and IPv6 rule sets having the same - name - - vyos_firewall_rules - Add support for log enable on individual rules - - vyos_firewall_rules - fixed incorrect option 'disabled' passed to the rules. + - Add vyos_hostname resource module. + - Rename V4-EGRESS/V6-EGRESS to EGRESS in the tests to test the same-name situation + - Update vyos_facts to support IPv4 and IPv6 rule sets having the same name + - Update vyos_firewall_rules to support IPv4 and IPv6 rule sets having the same + name + - vyos_firewall_rules - Add support for log enable on individual rules + - vyos_firewall_rules - fixed incorrect option 'disabled' passed to the rules. fragments: - - rule_set_same_name.yaml - - vyos_firewall_rules_add_log.yaml - - vyos_firewall_rules_fix_rules_disable.yaml - - vyos_hostname_rm.yaml + - rule_set_same_name.yaml + - vyos_firewall_rules_add_log.yaml + - vyos_firewall_rules_fix_rules_disable.yaml + - vyos_hostname_rm.yaml modules: - - description: Manages hostname resource module - name: vyos_hostname - namespace: '' - release_date: '2022-03-01' + - description: Manages hostname resource module + name: vyos_hostname + namespace: "" + release_date: "2022-03-01" 3.0.0: changes: bugfixes: - - Add symlink of modules under plugins/action + - Add symlink of modules under plugins/action major_changes: - - Minimum required ansible.netcommon version is 2.5.1. - - Updated base plugin references to ansible.netcommon. - - '`vyos_facts` - change default gather_subset to `min` from `!config` (https://github.com/ansible-collections/vyos.vyos/issues/231).' + - Minimum required ansible.netcommon version is 2.5.1. + - Updated base plugin references to ansible.netcommon. + - "`vyos_facts` - change default gather_subset to `min` from `!config` (https://github.com/ansible-collections/vyos.vyos/issues/231)." minor_changes: - - Change preconfig hostname from vyos to vyosuser + - Change preconfig hostname from vyos to vyosuser fragments: - - add_fqcn_ansible_connection.yaml - - add_symlinks_redirection.yml - - change_host_name.yml - - netcommon_ref_update.yaml - - vyos_facts_remove_warning.yaml - - vyos_facts_update.yaml - release_date: '2022-04-26' + - add_fqcn_ansible_connection.yaml + - add_symlinks_redirection.yml + - change_host_name.yml + - netcommon_ref_update.yaml + - vyos_facts_remove_warning.yaml + - vyos_facts_update.yaml + release_date: "2022-04-26" 3.0.1: changes: minor_changes: - - firewall_rules - icmpv6 type - add support for vyos sw >= 1.4. + - firewall_rules - icmpv6 type - add support for vyos sw >= 1.4. fragments: - - firewall_rule_cli_change_version_1.4.yaml - release_date: '2022-05-25' + - firewall_rule_cli_change_version_1.4.yaml + release_date: "2022-05-25" 4.0.0: changes: major_changes: - - 'Use of connection: local and the provider option are no longer valid on any - modules in this collection.' + - "Use of connection: local and the provider option are no longer valid on any + modules in this collection." minor_changes: - - Update fact gathering to support v1.3 show version output + - Update fact gathering to support v1.3 show version output removed_features: - - vyos_interface - use vyos_interfaces instead. - - vyos_l3_interface - use vyos_l3_interfaces instead. - - vyos_linkagg - use vyos_lag_interfaces instead. - - vyos_lldp - use vyos_lldp_global instead. - - vyos_lldp_interface - use vyos_lldp_interfaces instead. - - vyos_static_route - use vyos_static_routes instead. + - vyos_interface - use vyos_interfaces instead. + - vyos_l3_interface - use vyos_l3_interfaces instead. + - vyos_linkagg - use vyos_lag_interfaces instead. + - vyos_lldp - use vyos_lldp_global instead. + - vyos_lldp_interface - use vyos_lldp_interfaces instead. + - vyos_static_route - use vyos_static_routes instead. fragments: - - 271_equuleus_facts.yaml - - 2H22-removal.yaml - - fix-precommit-issues.yml - - fqcn.yaml - - gha.yaml - - ignores-2.15.yaml - - net_mods.yaml - - renamed.yaml - release_date: '2022-10-13' + - 271_equuleus_facts.yaml + - 2H22-removal.yaml + - fix-precommit-issues.yml + - fqcn.yaml + - gha.yaml + - ignores-2.15.yaml + - net_mods.yaml + - renamed.yaml + release_date: "2022-10-13" diff --git a/changelogs/config.yaml b/changelogs/config.yaml index 5818697..7a1e3ed 100644 --- a/changelogs/config.yaml +++ b/changelogs/config.yaml @@ -1,30 +1,30 @@ changelog_filename_template: ../CHANGELOG.rst changelog_filename_version_depth: 0 changes_file: changelog.yaml changes_format: combined keep_fragments: false mention_ancestor: true new_plugins_after_name: removed_features notesdir: fragments prelude_section_name: release_summary prelude_section_title: Release Summary flatmap: true sections: -- - major_changes - - Major Changes -- - minor_changes - - Minor Changes -- - breaking_changes - - Breaking Changes / Porting Guide -- - deprecated_features - - Deprecated Features -- - removed_features - - Removed Features (previously deprecated) -- - security_fixes - - Security Fixes -- - bugfixes - - Bugfixes -- - known_issues - - Known Issues + - - major_changes + - Major Changes + - - minor_changes + - Minor Changes + - - breaking_changes + - Breaking Changes / Porting Guide + - - deprecated_features + - Deprecated Features + - - removed_features + - Removed Features (previously deprecated) + - - security_fixes + - Security Fixes + - - bugfixes + - Bugfixes + - - known_issues + - Known Issues title: Vyos Collection trivial_section_name: trivial diff --git a/changelogs/fragments/prettier.yaml b/changelogs/fragments/prettier.yaml new file mode 100644 index 0000000..53a9359 --- /dev/null +++ b/changelogs/fragments/prettier.yaml @@ -0,0 +1,3 @@ +--- +trivial: + - Add isort and prettier pre-commit hooks diff --git a/meta/runtime.yml b/meta/runtime.yml index 8c7c766..02e7896 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,67 +1,67 @@ --- requires_ansible: ">=2.9.10" plugin_routing: modules: banner: redirect: vyos.vyos.vyos_banner bgp_global: redirect: vyos.vyos.vyos_bgp_global bgp_address_family: redirect: vyos.vyos.vyos_bgp_address_family command: redirect: vyos.vyos.vyos_command config: redirect: vyos.vyos.vyos_config facts: redirect: vyos.vyos.vyos_facts firewall_global: redirect: vyos.vyos.vyos_firewall_global firewall_interfaces: redirect: vyos.vyos.vyos_firewall_interfaces firewall_rules: redirect: vyos.vyos.vyos_firewall_rules hostname: redirect: vyos.vyos.vyos_hostname interfaces: redirect: vyos.vyos.vyos_interfaces l3_interfaces: redirect: vyos.vyos.vyos_l3_interfaces lag_interfaces: redirect: vyos.vyos.vyos_lag_interfaces lldp_global: redirect: vyos.vyos.vyos_lldp_global lldp_interfaces: redirect: vyos.vyos.vyos_lldp_interfaces logging: redirect: vyos.vyos.vyos_logging deprecation: - removal_date: '2023-08-01' + removal_date: "2023-08-01" warning_text: See the plugin documentation for more details vyos_logging: deprecation: - removal_date: '2023-08-01' + removal_date: "2023-08-01" warning_text: See the plugin documentation for more details logging_global: redirect: vyos.vyos.vyos_logging_global ntp_global: redirect: vyos.vyos.vyos_ntp_global ospfv2: redirect: vyos.vyos.vyos_ospfv2 ospfv3: redirect: vyos.vyos.vyos_ospfv3 ospf_interfaces: redirect: vyos.vyos.vyos_ospf_interfaces ping: redirect: vyos.vyos.vyos_ping prefix_lists: redirect: vyos.vyos.vyos_prefix_lists snmp_server: redirect: vyos.vyos.vyos_snmp_servers static_routes: redirect: vyos.vyos.vyos_static_routes system: redirect: vyos.vyos.vyos_system user: redirect: vyos.vyos.vyos_user vlan: redirect: vyos.vyos.vyos_vlan diff --git a/plugins/action/vyos.py b/plugins/action/vyos.py index f6f9aad..df8e712 100644 --- a/plugins/action/vyos.py +++ b/plugins/action/vyos.py @@ -1,55 +1,53 @@ # # (c) 2016 Red Hat Inc. # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import absolute_import, division, print_function __metaclass__ = type +from ansible.utils.display import Display from ansible_collections.ansible.netcommon.plugins.action.network import ( ActionModule as ActionNetworkModule, ) -from ansible.utils.display import Display display = Display() class ActionModule(ActionNetworkModule): def run(self, tmp=None, task_vars=None): del tmp # tmp no longer has any effect module_name = self._task.action.split(".")[-1] - self._config_module = ( - True if module_name in ["vyos_config", "config"] else False - ) + self._config_module = True if module_name in ["vyos_config", "config"] else False persistent_connection = self._play_context.connection.split(".")[-1] warnings = [] if persistent_connection != "network_cli": return { "failed": True, "msg": "Connection type %s is not valid for this module" % self._play_context.connection, } result = super(ActionModule, self).run(task_vars=task_vars) if warnings: if "warnings" in result: result["warnings"].extend(warnings) else: result["warnings"] = warnings return result diff --git a/plugins/cliconf/vyos.py b/plugins/cliconf/vyos.py index 885cf1f..4916d52 100644 --- a/plugins/cliconf/vyos.py +++ b/plugins/cliconf/vyos.py @@ -1,363 +1,336 @@ # (c) 2017 Red Hat Inc. # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ author: Ansible Networking Team (@ansible-network) name: vyos short_description: Use vyos cliconf to run command on VyOS platform description: - This vyos plugin provides low level abstraction apis for sending and receiving CLI commands from VyOS network devices. version_added: 1.0.0 options: config_commands: description: - Specifies a list of commands that can make configuration changes to the target device. - When `ansible_network_single_user_mode` is enabled, if a command sent to the device is present in this list, the existing cache is invalidated. version_added: 2.0.0 type: list elements: str default: [] vars: - name: ansible_vyos_config_commands """ -import re import json +import re from ansible.errors import AnsibleConnectionFailure from ansible.module_utils._text import to_text from ansible.module_utils.common._collections_compat import Mapping from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( NetworkConfig, ) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, -) -from ansible_collections.ansible.netcommon.plugins.plugin_utils.cliconf_base import ( - CliconfBase, -) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list +from ansible_collections.ansible.netcommon.plugins.plugin_utils.cliconf_base import CliconfBase class Cliconf(CliconfBase): __rpc__ = CliconfBase.__rpc__ + [ "commit", "discard_changes", "get_diff", "run_commands", ] def __init__(self, *args, **kwargs): super(Cliconf, self).__init__(*args, **kwargs) self._device_info = {} def get_device_info(self): if not self._device_info: device_info = {} device_info["network_os"] = "vyos" reply = self.get("show version") data = to_text(reply, errors="surrogate_or_strict").strip() match = re.search(r"Version:\s*(.*)", data) if match: device_info["network_os_version"] = match.group(1) match = re.search(r"(?:HW|Hardware) model:\s*(\S+)", data) if match: device_info["network_os_model"] = match.group(1) reply = self.get("show host name") device_info["network_os_hostname"] = to_text( reply, errors="surrogate_or_strict" ).strip() self._device_info = device_info return self._device_info def get_config(self, flags=None, format=None): if format: option_values = self.get_option_values() if format not in option_values["format"]: raise ValueError( "'format' value %s is invalid. Valid values of format are %s" % (format, ", ".join(option_values["format"])) ) if not flags: flags = [] if format == "text": command = "show configuration" else: command = "show configuration commands" command += " ".join(to_list(flags)) command = command.strip() out = self.send_command(command) return out - def edit_config( - self, candidate=None, commit=True, replace=None, comment=None - ): + def edit_config(self, candidate=None, commit=True, replace=None, comment=None): resp = {} operations = self.get_device_operations() - self.check_edit_config_capability( - operations, candidate, commit, replace, comment - ) + self.check_edit_config_capability(operations, candidate, commit, replace, comment) results = [] requests = [] self.send_command("configure") for cmd in to_list(candidate): if not isinstance(cmd, Mapping): cmd = {"command": cmd} results.append(self.send_command(**cmd)) requests.append(cmd["command"]) out = self.get("compare") out = to_text(out, errors="surrogate_or_strict") diff_config = out if not out.startswith("No changes") else None if diff_config: if commit: try: self.commit(comment) except AnsibleConnectionFailure as e: msg = "commit failed: %s" % e.message self.discard_changes() raise AnsibleConnectionFailure(msg) else: self.send_command("exit") else: self.discard_changes() else: self.send_command("exit") if ( - to_text( - self._connection.get_prompt(), errors="surrogate_or_strict" - ) + to_text(self._connection.get_prompt(), errors="surrogate_or_strict") .strip() .endswith("#") ): self.discard_changes() if diff_config: resp["diff"] = diff_config resp["response"] = results resp["request"] = requests return resp def get( self, command=None, prompt=None, answer=None, sendonly=False, newline=True, output=None, check_all=False, ): if not command: raise ValueError("must provide value of command to execute") if output: - raise ValueError( - "'output' value %s is not supported for get" % output - ) + raise ValueError("'output' value %s is not supported for get" % output) return self.send_command( command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all, ) def commit(self, comment=None): if comment: command = 'commit comment "{0}"'.format(comment) else: command = "commit" self.send_command(command) def discard_changes(self): self.send_command("exit discard") def get_diff( self, candidate=None, running=None, diff_match="line", diff_ignore_lines=None, path=None, diff_replace=None, ): diff = {} device_operations = self.get_device_operations() option_values = self.get_option_values() if candidate is None and device_operations["supports_generate_diff"]: - raise ValueError( - "candidate configuration is required to generate diff" - ) + raise ValueError("candidate configuration is required to generate diff") if diff_match not in option_values["diff_match"]: raise ValueError( "'match' value %s in invalid, valid values are %s" % (diff_match, ", ".join(option_values["diff_match"])) ) if diff_replace: raise ValueError("'replace' in diff is not supported") if diff_ignore_lines: raise ValueError("'diff_ignore_lines' in diff is not supported") if path: raise ValueError("'path' in diff is not supported") - set_format = candidate.startswith("set") or candidate.startswith( - "delete" - ) + set_format = candidate.startswith("set") or candidate.startswith("delete") candidate_obj = NetworkConfig(indent=4, contents=candidate) if not set_format: config = [c.line for c in candidate_obj.items] commands = list() # this filters out less specific lines for item in config: for index, entry in enumerate(commands): if item.startswith(entry): del commands[index] break commands.append(item) - candidate_commands = [ - "set %s" % cmd.replace(" {", "") for cmd in commands - ] + candidate_commands = ["set %s" % cmd.replace(" {", "") for cmd in commands] else: candidate_commands = str(candidate).strip().split("\n") if diff_match == "none": diff["config_diff"] = list(candidate_commands) return diff - running_commands = [ - str(c).replace("'", "") for c in running.splitlines() - ] + running_commands = [str(c).replace("'", "") for c in running.splitlines()] updates = list() visited = set() for line in candidate_commands: item = str(line).replace("'", "") if not item.startswith("set") and not item.startswith("delete"): - raise ValueError( - "line must start with either `set` or `delete`" - ) + raise ValueError("line must start with either `set` or `delete`") elif item.startswith("set") and item not in running_commands: updates.append(line) elif item.startswith("delete"): if not running_commands: updates.append(line) else: item = re.sub(r"delete", "set", item) for entry in running_commands: if entry.startswith(item) and line not in visited: updates.append(line) visited.add(line) diff["config_diff"] = list(updates) return diff def run_commands(self, commands=None, check_rc=True): if commands is None: raise ValueError("'commands' value is required") responses = list() for cmd in to_list(commands): if not isinstance(cmd, Mapping): cmd = {"command": cmd} output = cmd.pop("output", None) if output: - raise ValueError( - "'output' value %s is not supported for run_commands" - % output - ) + raise ValueError("'output' value %s is not supported for run_commands" % output) try: out = self.send_command(**cmd) except AnsibleConnectionFailure as e: if check_rc: raise out = getattr(e, "err", e) responses.append(out) return responses def get_device_operations(self): return { "supports_diff_replace": False, "supports_commit": True, "supports_rollback": False, "supports_defaults": False, "supports_onbox_diff": True, "supports_commit_comment": True, "supports_multiline_delimiter": False, "supports_diff_match": True, "supports_diff_ignore_lines": False, "supports_generate_diff": False, "supports_replace": False, } def get_option_values(self): return { "format": ["text", "set"], "diff_match": ["line", "none"], "diff_replace": [], "output": [], } def get_capabilities(self): result = super(Cliconf, self).get_capabilities() result["device_operations"] = self.get_device_operations() result.update(self.get_option_values()) return json.dumps(result) def set_cli_prompt_context(self): """ Make sure we are in the operational cli mode :return: None """ if self._connection.connected: - self._update_cli_prompt_context( - config_context="#", exit_command="exit discard" - ) + self._update_cli_prompt_context(config_context="#", exit_command="exit discard") diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py index 61d9a55..0e80031 100644 --- a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py @@ -1,269 +1,261 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The arg spec for the vyos_firewall_rules module """ from __future__ import absolute_import, division, print_function __metaclass__ = type class Firewall_rulesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_firewall_rules module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "afi": { "choices": ["ipv4", "ipv6"], "required": True, "type": "str", }, "rule_sets": { "elements": "dict", "options": { "default_action": { "choices": ["drop", "reject", "accept"], "type": "str", }, "description": {"type": "str"}, "enable_default_log": {"type": "bool"}, "name": {"type": "str"}, "rules": { "elements": "dict", "options": { "action": { "choices": [ "drop", "reject", "accept", "inspect", ], "type": "str", }, "description": {"type": "str"}, "destination": { "options": { "address": {"type": "str"}, "group": { "options": { - "address_group": { - "type": "str" - }, - "network_group": { - "type": "str" - }, + "address_group": {"type": "str"}, + "network_group": {"type": "str"}, "port_group": {"type": "str"}, }, "type": "dict", }, "port": {"type": "str"}, }, "type": "dict", }, "disable": { "type": "bool", "aliases": ["disabled"], }, "fragment": { "choices": [ "match-frag", "match-non-frag", ], "type": "str", }, "icmp": { "options": { "code": {"type": "int"}, "type": {"type": "int"}, "type_name": { "choices": [ "any", "echo-reply", "destination-unreachable", "network-unreachable", "host-unreachable", "protocol-unreachable", "port-unreachable", "fragmentation-needed", "source-route-failed", "network-unknown", "host-unknown", "network-prohibited", "host-prohibited", "TOS-network-unreachable", "TOS-host-unreachable", "communication-prohibited", "host-precedence-violation", "precedence-cutoff", "source-quench", "redirect", "network-redirect", "host-redirect", "TOS-network-redirect", "TOS-host-redirect", "echo-request", "router-advertisement", "router-solicitation", "time-exceeded", "ttl-zero-during-transit", "ttl-zero-during-reassembly", "parameter-problem", "ip-header-bad", "required-option-missing", "timestamp-request", "timestamp-reply", "address-mask-request", "address-mask-reply", "ping", "pong", "ttl-exceeded", ], "type": "str", }, }, "type": "dict", }, "ipsec": { "choices": ["match-ipsec", "match-none"], "type": "str", }, "limit": { "options": { "burst": {"type": "int"}, "rate": { "options": { "number": {"type": "int"}, "unit": {"type": "str"}, }, "type": "dict", }, }, "type": "dict", }, "log": { "type": "str", "choices": ["enable", "disable"], }, "number": {"required": True, "type": "int"}, "p2p": { "elements": "dict", "options": { "application": { "choices": [ "all", "applejuice", "bittorrent", "directconnect", "edonkey", "gnutella", "kazaa", ], "type": "str", } }, "type": "list", }, "protocol": {"type": "str"}, "recent": { "options": { "count": {"type": "int"}, "time": {"type": "int"}, }, "type": "dict", }, "source": { "options": { "address": {"type": "str"}, "group": { "options": { - "address_group": { - "type": "str" - }, - "network_group": { - "type": "str" - }, + "address_group": {"type": "str"}, + "network_group": {"type": "str"}, "port_group": {"type": "str"}, }, "type": "dict", }, "mac_address": {"type": "str"}, "port": {"type": "str"}, }, "type": "dict", }, "state": { "options": { "established": {"type": "bool"}, "invalid": {"type": "bool"}, "new": {"type": "bool"}, "related": {"type": "bool"}, }, "type": "dict", }, "tcp": { "options": {"flags": {"type": "str"}}, "type": "dict", }, "time": { "options": { "monthdays": {"type": "str"}, "startdate": {"type": "str"}, "starttime": {"type": "str"}, "stopdate": {"type": "str"}, "stoptime": {"type": "str"}, "utc": {"type": "bool"}, "weekdays": {"type": "str"}, }, "type": "dict", }, }, "type": "list", }, }, "type": "list", }, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py index 0061e79..6c83372 100644 --- a/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/argspec/ospfv2/ospfv2.py @@ -1,281 +1,279 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The arg spec for the vyos_ospfv2 module """ class Ospfv2Args(object): # pylint: disable=R0903 """The arg spec for the vyos_ospfv2 module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "options": { "auto_cost": { "options": {"reference_bandwidth": {"type": "int"}}, "type": "dict", }, "default_information": { "options": { "originate": { "options": { "always": {"type": "bool"}, "metric": {"type": "int"}, "metric_type": {"type": "int"}, "route_map": {"type": "str"}, }, "type": "dict", } }, "type": "dict", }, "default_metric": {"type": "int"}, "distance": { "options": { "global": {"type": "int"}, "ospf": { "options": { "external": {"type": "int"}, "inter_area": {"type": "int"}, "intra_area": {"type": "int"}, }, "type": "dict", }, }, "type": "dict", }, "log_adjacency_changes": { "choices": ["detail"], "type": "str", }, "max_metric": { "options": { "router_lsa": { "options": { "administrative": {"type": "bool"}, "on_shutdown": {"type": "int"}, "on_startup": {"type": "int"}, }, "type": "dict", } }, "type": "dict", }, "mpls_te": { "options": { "enabled": {"type": "bool"}, "router_address": {"type": "str"}, }, "type": "dict", }, "neighbor": { "elements": "dict", "options": { "neighbor_id": {"type": "str"}, "poll_interval": {"type": "int"}, "priority": {"type": "int"}, }, "type": "list", }, "areas": { "elements": "dict", "options": { "area_id": {"type": "str"}, "area_type": { "options": { "normal": {"type": "bool"}, "nssa": { "options": { "default_cost": {"type": "int"}, "no_summary": {"type": "bool"}, "set": {"type": "bool"}, "translate": { "choices": [ "always", "candidate", "never", ], "type": "str", }, }, "type": "dict", }, "stub": { "options": { "default_cost": {"type": "int"}, "no_summary": {"type": "bool"}, "set": {"type": "bool"}, }, "type": "dict", }, }, "type": "dict", }, "authentication": { "choices": ["plaintext-password", "md5"], "type": "str", }, "network": { "elements": "dict", - "options": { - "address": {"required": True, "type": "str"} - }, + "options": {"address": {"required": True, "type": "str"}}, "type": "list", }, "range": { "elements": "dict", "options": { "address": {"type": "str"}, "cost": {"type": "int"}, "not_advertise": {"type": "bool"}, "substitute": {"type": "str"}, }, "type": "list", }, "shortcut": { "choices": ["default", "disable", "enable"], "type": "str", }, "virtual_link": { "elements": "dict", "options": { "address": {"type": "str"}, "authentication": { "options": { "md5": { "elements": "dict", "options": { "key_id": {"type": "int"}, "md5_key": { "type": "str", "no_log": True, }, }, "type": "list", }, "plaintext_password": { "type": "str", "no_log": True, }, }, "type": "dict", }, "dead_interval": {"type": "int"}, "hello_interval": {"type": "int"}, "retransmit_interval": {"type": "int"}, "transmit_delay": {"type": "int"}, }, "type": "list", }, }, "type": "list", }, "parameters": { "options": { "abr_type": { "choices": [ "cisco", "ibm", "shortcut", "standard", ], "type": "str", }, "opaque_lsa": {"type": "bool"}, "rfc1583_compatibility": {"type": "bool"}, "router_id": {"type": "str"}, }, "type": "dict", }, "passive_interface": {"type": "list", "elements": "str"}, "passive_interface_exclude": { "type": "list", "elements": "str", }, "redistribute": { "elements": "dict", "options": { "metric": {"type": "int"}, "metric_type": {"type": "int"}, "route_map": {"type": "str"}, "route_type": { "choices": [ "bgp", "connected", "kernel", "rip", "static", ], "type": "str", }, }, "type": "list", }, "route_map": {"type": "list", "elements": "str"}, "timers": { "options": { "refresh": { "options": {"timers": {"type": "int"}}, "type": "dict", }, "throttle": { "options": { "spf": { "options": { "delay": {"type": "int"}, "initial_holdtime": {"type": "int"}, "max_holdtime": {"type": "int"}, }, "type": "dict", } }, "type": "dict", }, }, "type": "dict", }, }, "type": "dict", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "deleted", "parsed", "gathered", "rendered", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py index 790f5e6..6baa434 100644 --- a/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/argspec/route_maps/route_maps.py @@ -1,230 +1,228 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_route_maps module """ class Route_mapsArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_route_maps module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "type": "list", "elements": "dict", "options": { "route_map": {"type": "str"}, "entries": { "aliases": ["rules"], "type": "list", "elements": "dict", "options": { "sequence": {"type": "int"}, "call": {"type": "str"}, "description": {"type": "str"}, "action": { "type": "str", "choices": ["deny", "permit"], }, "continue_sequence": {"type": "int"}, "set": { "type": "dict", "options": { "aggregator": { "type": "dict", "options": { "ip": {"type": "str"}, "as": {"type": "str"}, }, }, "as_path_exclude": {"type": "str"}, "as_path_prepend": {"type": "str"}, "atomic_aggregate": {"type": "bool"}, "bgp_extcommunity_rt": {"type": "str"}, "comm_list": { "type": "dict", "options": { "comm_list": {"type": "str"}, "delete": {"type": "bool"}, }, }, "community": { "type": "dict", "options": {"value": {"type": "str"}}, }, "extcommunity_rt": {"type": "str"}, "extcommunity_soo": {"type": "str"}, "ip_next_hop": {"type": "str"}, "ipv6_next_hop": { "type": "dict", "options": { "ip_type": { "type": "str", "choices": ["global", "local"], }, "value": {"type": "str"}, }, }, "large_community": {"type": "str"}, "local_preference": {"type": "str"}, "metric": {"type": "str"}, "metric_type": { "type": "str", "choices": ["type-1", "type-2"], }, "origin": { "type": "str", "choices": ["egp", "igp", "incomplete"], }, "originator_id": {"type": "str"}, "src": {"type": "str"}, "tag": {"type": "str"}, "weight": {"type": "str"}, }, }, "match": { "type": "dict", "options": { "as_path": {"type": "str"}, "community": { "type": "dict", "options": { "community_list": {"type": "str"}, "exact_match": {"type": "bool"}, }, }, "extcommunity": {"type": "str"}, "interface": {"type": "str"}, "ip": { "type": "dict", "options": { "address": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, "next_hop": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, "route_source": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, }, }, "ipv6": { "type": "dict", "options": { "address": { "type": "dict", "options": { "list_type": { "type": "str", "choices": [ "access-list", "prefix-list", ], }, "value": {"type": "str"}, }, }, "next_hop": {"type": "str"}, }, }, - "large_community_large_community_list": { - "type": "str" - }, + "large_community_large_community_list": {"type": "str"}, "metric": {"type": "int"}, "origin": { "type": "str", "choices": ["ebgp", "ibgp", "incomplete"], }, "peer": {"type": "str"}, "rpki": { "type": "str", "choices": [ "notfound", "invalid", "valid", ], }, }, }, "on_match": { "type": "dict", "options": { "next": {"type": "bool"}, "goto": {"type": "int"}, }, }, }, }, }, }, "running_config": {"type": "str"}, "state": { "type": "str", "choices": [ "deleted", "merged", "overridden", "replaced", "gathered", "rendered", "parsed", ], "default": "merged", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py index a62e6a5..53defde 100644 --- a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py @@ -1,369 +1,354 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_bgp_address_family config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ import re from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import ( Bgp_address_familyTemplate, ) class Bgp_address_family(ResourceModule): """ The vyos_bgp_address_family config class """ def __init__(self, module): super(Bgp_address_family, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="bgp_address_family", tmplt=Bgp_address_familyTemplate(), ) self.parsers = [] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} - if ( - self.want.get("as_number") == self.have.get("as_number") - or not self.have - ): + if self.want.get("as_number") == self.have.get("as_number") or not self.have: if self.want: wantd = {self.want["as_number"]: self.want} if self.have: haved = {self.have["as_number"]: self.have} else: - self._module.fail_json( - msg="Only one bgp instance is allowed per device" - ) + self._module.fail_json(msg="Only one bgp instance is allowed per device") # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._bgp_af_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": for k, have in iteritems(haved): self._delete_af(wantd, have) wantd = {} if self.state == "overridden": for k, have in iteritems(haved): if k not in wantd: self._compare(want={}, have=have) for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Bgp_address_family network resource. """ self._compare_af(want, have) self._compare_neighbors(want, have) # Do the negation first command_set = [] for cmd in self.commands: if cmd not in command_set: if "delete" in cmd: command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare_af(self, want, have): waf = want.get("address_family", {}) haf = have.get("address_family", {}) for name, entry in iteritems(waf): self._compare_lists( entry, have=haf.get(name, {}), as_number=want["as_number"], afi=name, ) for name, entry in iteritems(haf): if name not in waf.keys() and self.state == "replaced": continue - self._compare_lists( - {}, entry, as_number=have["as_number"], afi=name - ) + self._compare_lists({}, entry, as_number=have["as_number"], afi=name) def _delete_af(self, want, have): for as_num, entry in iteritems(want): for afi, af_entry in iteritems(entry.get("address_family", {})): if have.get("address_family"): for hafi, hentry in iteritems(have["address_family"]): if hafi == afi: self.commands.append( self._tmplt.render( { "as_number": as_num, "address_family": {"afi": afi}, }, "address_family", True, ) ) for neigh, neigh_entry in iteritems(entry.get("neighbors", {})): if have.get("neighbors"): for hneigh, hnentry in iteritems(have["neighbors"]): if hneigh == neigh: if not neigh_entry.get("address_family"): self.commands.append( self._tmplt.render( { "as_number": as_num, - "neighbors": { - "neighbor_address": neigh - }, + "neighbors": {"neighbor_address": neigh}, }, "neighbors", True, ) ) else: for k in neigh_entry["address_family"].keys(): if ( hnentry.get("address_family") - and k - in hnentry["address_family"].keys() + and k in hnentry["address_family"].keys() ): self.commands.append( self._tmplt.render( { "as_number": as_num, "neighbors": { "neighbor_address": neigh, - "address_family": { - "afi": k - }, + "address_family": {"afi": k}, }, }, "neighbors.address_family", True, ) ) def _compare_neighbors(self, want, have): parsers = [ "neighbors.allowas_in", "neighbors.as_override", "neighbors.attribute_unchanged.as_path", "neighbors.attribute_unchanged.med", "neighbors.attribute_unchanged.next_hop", "neighbors.capability_dynamic", "neighbors.capability_orf", "neighbors.default_originate", "neighbors.distribute_list", "neighbors.prefix_list", "neighbors.filter_list", "neighbors.maximum_prefix", "neighbors.nexthop_local", "neighbors.nexthop_self", "neighbors.peer_group", "neighbors.remove_private_as", "neighbors.route_map", "neighbors.route_reflector_client", "neighbors.route_server_client", "neighbors.soft_reconfiguration", "neighbors.unsuppress_map", "neighbors.weight", ] wneigh = want.get("neighbors", {}) hneigh = have.get("neighbors", {}) for name, entry in iteritems(wneigh): for afi, af_entry in iteritems(entry.get("address_family")): for k, val in iteritems(af_entry): w = { "as_number": want["as_number"], "neighbors": { "neighbor_address": name, "address_family": {"afi": afi, k: val}, }, } h = {} if hneigh.get(name): if hneigh[name]["address_family"].get(afi): if hneigh[name]["address_family"][afi].get(k): h = { "as_number": want["as_number"], "neighbors": { "neighbor_address": name, "address_family": { "afi": afi, - k: hneigh[name]["address_family"][ - afi - ].pop(k, {}), + k: hneigh[name]["address_family"][afi].pop(k, {}), }, }, } self.compare( parsers=parsers, want=w, have=h, ) for name, entry in iteritems(hneigh): if name not in wneigh.keys(): # remove surplus config for overridden and replaced if self.state != "replaced": self.commands.append( self._tmplt.render( { "as_number": have["as_number"], "neighbors": {"neighbor_address": name}, }, "neighbors", True, ) ) continue for hafi, haf_entry in iteritems(entry.get("address_family")): # remove surplus configs for given neighbor - replace and overridden for k, val in iteritems(haf_entry): h = { "as_number": have["as_number"], "neighbors": { "neighbor_address": name, "address_family": {"afi": hafi, k: val}, }, } self.compare(parsers=parsers, want={}, have=h) def _compare_lists(self, want, have, as_number, afi): parsers = [ "aggregate_address", "network.backdoor", "network.path_limit", "network.route_map", "redistribute.metric", "redistribute.route_map", "redistribute.table", ] for attrib in ["redistribute", "networks", "aggregate_address"]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in iteritems(wdict): if entry != hdict.get(key, {}): self.compare( parsers=parsers, want={ "as_number": as_number, "address_family": {"afi": afi, attrib: entry}, }, have={ "as_number": as_number, "address_family": { "afi": afi, attrib: hdict.pop(key, {}), }, }, ) hdict.pop(key, {}) # remove remaining items in have for replaced if not wdict and hdict: attrib = re.sub("_", "-", attrib) attrib = re.sub("networks", "network", attrib) self.commands.append( "delete protocols bgp " + str(as_number) + " " + "address-family " + afi + " " + attrib ) hdict = {} for key, entry in iteritems(hdict): self.compare( parsers=parsers, want={}, have={ "as_number": as_number, "address_family": {"afi": afi, attrib: entry}, }, ) def _bgp_af_list_to_dict(self, entry): for name, proc in iteritems(entry): if "address_family" in proc: af_dict = {} for entry in proc.get("address_family"): if "networks" in entry: network_dict = {} for n_entry in entry.get("networks", []): network_dict.update({n_entry["prefix"]: n_entry}) entry["networks"] = network_dict if "aggregate_address" in entry: agg_dict = {} for a_entry in entry.get("aggregate_address", []): agg_dict.update({a_entry["prefix"]: a_entry}) entry["aggregate_address"] = agg_dict if "redistribute" in entry: redis_dict = {} for r_entry in entry.get("redistribute", []): proto_key = r_entry.get("protocol", "table") redis_dict.update({proto_key: r_entry}) entry["redistribute"] = redis_dict for af in proc.get("address_family"): af_dict.update({af["afi"]: af}) proc["address_family"] = af_dict if "neighbors" in proc: neigh_dict = {} for entry in proc.get("neighbors", []): neigh_dict.update({entry["neighbor_address"]: entry}) proc["neighbors"] = neigh_dict self._bgp_af_list_to_dict(proc["neighbors"]) diff --git a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py index f8de74e..96c640f 100644 --- a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py @@ -1,426 +1,407 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_bgp_global config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ import re + from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import ( Bgp_globalTemplate, ) class Bgp_global(ResourceModule): """ The vyos_bgp_global config class """ def __init__(self, module): super(Bgp_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="bgp_global", tmplt=Bgp_globalTemplate(), ) self.parsers = [] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} - if ( - self.want.get("as_number") == self.have.get("as_number") - or not self.have - ): + if self.want.get("as_number") == self.have.get("as_number") or not self.have: if self.want: wantd = {self.want["as_number"]: self.want} if self.have: haved = {self.have["as_number"]: self.have} else: - self._module.fail_json( - msg="Only one bgp instance is allowed per device" - ) + self._module.fail_json(msg="Only one bgp instance is allowed per device") # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._bgp_global_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "purged": h_del = {} for k, v in iteritems(haved): if k in wantd or not wantd: h_del.update({k: v}) for num, entry in iteritems(h_del): - self.commands.append( - self._tmplt.render({"as_number": num}, "router", True) - ) + self.commands.append(self._tmplt.render({"as_number": num}, "router", True)) wantd = {} if self.state == "deleted": self._compare(want={}, have=self.have) wantd = {} for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Bgp_global network resource. """ parsers = ["maximum_paths", "timers"] self._compare_neighbor(want, have) self._compare_lists(want, have) self._compare_bgp_params(want, have) for name, entry in iteritems(want): if name != "as_number": self.compare( parsers=parsers, want={"as_number": want["as_number"], name: entry}, have={ "as_number": want["as_number"], name: have.pop(name, {}), }, ) for name, entry in iteritems(have): if name != "as_number": self.compare( parsers=parsers, want={}, have={"as_number": have["as_number"], name: entry}, ) # Do the negation first command_set = [] for cmd in self.commands: if cmd not in command_set: if "delete" in cmd: command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare_neighbor(self, want, have): parsers = [ "neighbor.advertisement_interval", "neighbor.allowas_in", "neighbor.as_override", "neighbor.attribute_unchanged.as_path", "neighbor.attribute_unchanged.med", "neighbor.attribute_unchanged.next_hop", "neighbor.capability_dynamic", "neighbor.capability_orf", "neighbor.default_originate", "neighbor.description", "neighbor.disable_capability_negotiation", "neighbor.disable_connected_check", "neighbor.disable_send_community", "neighbor.distribute_list", "neighbor.ebgp_multihop", "neighbor.filter_list", "neighbor.local_as", "neighbor.maximum_prefix", "neighbor.nexthop_self", "neighbor.override_capability", "neighbor.passive", "neighbor.password", "neighbor.peer_group_name", "neighbor.port", "neighbor.prefix_list", "neighbor.remote_as", "neighbor.remove_private_as", "neighbor.route_map", "neighbor.route_reflector_client", "neighbor.route_server_client", "neighbor.shutdown", "neighbor.soft_reconfiguration", "neighbor.strict_capability_match", "neighbor.unsuppress_map", "neighbor.update_source", "neighbor.weight", "neighbor.ttl_security", "neighbor.timers", "network.backdoor", "network.route_map", ] wneigh = want.pop("neighbor", {}) hneigh = have.pop("neighbor", {}) self._compare_neigh_lists(wneigh, hneigh) for name, entry in iteritems(wneigh): for k, v in entry.items(): if k == "address": continue if hneigh.get(name): h = {"address": name, k: hneigh[name].pop(k, {})} else: h = {} self.compare( parsers=parsers, want={ "as_number": want["as_number"], "neighbor": {"address": name, k: v}, }, have={"as_number": want["as_number"], "neighbor": h}, ) for name, entry in iteritems(hneigh): if name not in wneigh.keys(): if self._check_af(name): msg = "Use the _bgp_address_family module to delete the address_family under neighbor {0}, before replacing/deleting the neighbor.".format( name ) self._module.fail_json(msg=msg) else: self.commands.append( - "delete protocols bgp " - + str(have["as_number"]) - + " neighbor " - + name + "delete protocols bgp " + str(have["as_number"]) + " neighbor " + name ) continue for k, v in entry.items(): self.compare( parsers=parsers, want={}, have={ "as_number": have["as_number"], "neighbor": {"address": name, k: v}, }, ) def _compare_bgp_params(self, want, have): parsers = [ "bgp_params.always_compare_med", "bgp_params.bestpath.as_path", "bgp_params.bestpath.compare_routerid", "bgp_params.bestpath.med", "bgp_params.cluster_id", "bgp_params.confederation", "bgp_params.dampening_half_life", "bgp_params.dampening_max_suppress_time", "bgp_params.dampening_re_use", "bgp_params.dampening_start_suppress_time", "bgp_params.default", "bgp_params.deterministic_med", "bgp_params.disbale_network_import_check", "bgp_params.enforce_first_as", "bgp_params.graceful_restart", "bgp_params.log_neighbor_changes", "bgp_params.no_client_to_client_reflection", "bgp_params.no_fast_external_failover", "bgp_params.routerid", "bgp_params.scan_time", ] wbgp = want.pop("bgp_params", {}) hbgp = have.pop("bgp_params", {}) for name, entry in iteritems(wbgp): if name == "confederation": if entry != hbgp.pop(name, {}): self.addcmd( { "as_number": want["as_number"], "bgp_params": {name: entry}, }, "bgp_params.confederation", False, ) elif name == "distance": if entry != hbgp.pop(name, {}): distance_parsers = [ "bgp_params.distance.global", "bgp_params.distance.prefix", ] for distance_type in entry: self.compare( parsers=distance_parsers, want={ "as_number": want["as_number"], "bgp_params": {name: distance_type}, }, have={ "as_number": want["as_number"], "bgp_params": {name: hbgp.pop(name, {})}, }, ) else: self.compare( parsers=parsers, want={ "as_number": want["as_number"], "bgp_params": {name: entry}, }, have={ "as_number": want["as_number"], "bgp_params": {name: hbgp.pop(name, {})}, }, ) if not wbgp and hbgp: - self.commands.append( - "delete protocols bgp " - + str(have["as_number"]) - + " parameters" - ) + self.commands.append("delete protocols bgp " + str(have["as_number"]) + " parameters") hbgp = {} for name, entry in iteritems(hbgp): if name == "confederation": self.commands.append( - "delete protocols bgp " - + str(have["as_number"]) - + " parameters confederation" + "delete protocols bgp " + str(have["as_number"]) + " parameters confederation" ) elif name == "distance": distance_parsers = [ "bgp_params.distance.global", "bgp_params.distance.prefix", ] self.compare( parsers=distance_parsers, want={}, have={ "as_number": have["as_number"], "bgp_params": {name: entry[0]}, }, ) else: self.compare( parsers=parsers, want={}, have={ "as_number": have["as_number"], "bgp_params": {name: entry}, }, ) def _compare_lists(self, want, have): parsers = [ "network.backdoor", "network.route_map", "redistribute.metric", "redistribute.route_map", "aggregate_address", ] for attrib in ["redistribute", "network", "aggregate_address"]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in iteritems(wdict): if entry != hdict.get(key, {}): self.compare( parsers=parsers, want={"as_number": want["as_number"], attrib: entry}, have=hdict.pop(key, {}), ) hdict.pop(key, {}) # remove remaining items in have for replaced if not wdict and hdict: attrib = re.sub("_", "-", attrib) self.commands.append( - "delete protocols bgp " - + str(have["as_number"]) - + " " - + attrib + "delete protocols bgp " + str(have["as_number"]) + " " + attrib ) hdict = {} for key, entry in iteritems(hdict): self.compare( parsers=parsers, want={}, have={"as_number": have["as_number"], attrib: entry}, ) def _compare_neigh_lists(self, want, have): for attrib in [ "distribute_list", "filter_list", "prefix_list", "route_map", ]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in iteritems(wdict): if entry != hdict.pop(key, {}): self.addcmd(entry, "neighbor.{0}".format(attrib), False) # remove remaining items in have for replaced for entry in hdict.values(): self.addcmd(entry, "neighbor.{0}".format(attrib), True) def _bgp_global_list_to_dict(self, entry): for name, proc in iteritems(entry): if "neighbor" in proc: neigh_dict = {} for entry in proc.get("neighbor", []): neigh_dict.update({entry["address"]: entry}) proc["neighbor"] = neigh_dict if "network" in proc: network_dict = {} for entry in proc.get("network", []): network_dict.update({entry["address"]: entry}) proc["network"] = network_dict if "aggregate_address" in proc: agg_dict = {} for entry in proc.get("aggregate_address", []): agg_dict.update({entry["prefix"]: entry}) proc["aggregate_address"] = agg_dict if "redistribute" in proc: redis_dict = {} for entry in proc.get("redistribute", []): redis_dict.update({entry["protocol"]: entry}) proc["redistribute"] = redis_dict def _check_af(self, neighbor): af_present = False if self._connection: config_lines = self._get_config(self._connection).splitlines() for line in config_lines: if "address-family" in line: af_present = True return af_present def _get_config(self, connection): return connection.get( 'show configuration commands | match "set protocols bgp .* neighbor"' ) diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py index be8b172..079cba6 100644 --- a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py @@ -1,812 +1,718 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_firewall_global class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, remove_empties, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( list_diff_want_only, ) class Firewall_global(ConfigBase): """ The vyos_firewall_global class """ gather_subset = ["!all", "!min"] gather_network_resources = ["firewall_global"] def __init__(self, module): super(Firewall_global, self).__init__(module) def get_firewall_global_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - firewall_global_facts = facts["ansible_network_resources"].get( - "firewall_global" - ) + firewall_global_facts = facts["ansible_network_resources"].get("firewall_global") if not firewall_global_facts: return [] return firewall_global_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_firewall_global_facts = self.get_firewall_global_facts() else: existing_firewall_global_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_firewall_global_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_firewall_global_facts = self.get_firewall_global_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_firewall_global_facts( - data=running_config - ) + result["parsed"] = self.get_firewall_global_facts(data=running_config) else: changed_firewall_global_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_global_facts if result["changed"]: result["after"] = changed_firewall_global_facts elif self.state == "gathered": result["gathered"] = changed_firewall_global_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_global_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_firewall_global_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "rendered") and not w: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "deleted": commands.extend(self._state_deleted(want=None, have=h)) elif w: if self.state == "merged" or self.state == "rendered": commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def _state_replaced(self, w, h): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if h: commands.extend(self._state_deleted(h, w)) commands.extend(self._state_merged(w, h)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._add_global_attr(want, have)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] b_set = ( "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ) if want: for key, val in iteritems(want): if val and key in b_set and not have: commands.append(self._form_attr_cmd(attr=key, opr=False)) - elif ( - val - and key in b_set - and have - and key in have - and have[key] != val - ): + elif val and key in b_set and have and key in have and have[key] != val: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: commands.extend(self._render_attr_config(want, have, key)) elif not want and have: commands.append(self._compute_command(opr=False)) elif have: for key, val in iteritems(have): if val and key in b_set: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: commands.extend(self._render_attr_config(want, have, key)) return commands def _render_attr_config(self, w, h, key, opr=False): """ This function invoke the function to extend commands based on the key. :param w: the desired configuration. :param h: the current configuration. :param key: attribute name :param opr: operation :return: list of commands """ commands = [] if key == "ping": commands.extend(self._render_ping(key, w, h, opr=opr)) elif key == "group": commands.extend(self._render_group(key, w, h, opr=opr)) elif key == "state_policy": commands.extend(self._render_state_policy(key, w, h, opr=opr)) elif key == "route_redirects": commands.extend(self._render_route_redirects(key, w, h, opr=opr)) return commands def _add_global_attr(self, w, h, opr=True): """ This function forms the set/delete commands based on the 'opr' type for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w_fg = deepcopy(remove_empties(w)) l_set = ( "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ) if w_fg: for key, val in iteritems(w_fg): - if ( - opr - and key in l_set - and not (h and self._is_w_same(w_fg, h, key)) - ): + if opr and key in l_set and not (h and self._is_w_same(w_fg, h, key)): commands.append( - self._form_attr_cmd( - attr=key, val=self._bool_to_str(val), opr=opr - ) + self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr) ) elif not opr: if key and self._is_del(l_set, h): commands.append( - self._form_attr_cmd( - attr=key, key=self._bool_to_str(val), opr=opr - ) + self._form_attr_cmd(attr=key, key=self._bool_to_str(val), opr=opr) ) continue if ( key in l_set and not (h and self._in_target(h, key)) and not self._is_del(l_set, h) ): commands.append( - self._form_attr_cmd( - attr=key, val=self._bool_to_str(val), opr=opr - ) + self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr) ) else: - commands.extend( - self._render_attr_config(w_fg, h, key, opr) - ) + commands.extend(self._render_attr_config(w_fg, h, key, opr)) return commands def _render_ping(self, attr, w, h, opr): """ This function forms the commands for 'ping' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired configuration. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_ping = {} l_set = ("all", "broadcast") if h: h_ping = h.get(attr) or {} if self._is_root_del(w[attr], h_ping, attr): for item, value in iteritems(h[attr]): if not opr and item in l_set: commands.append(self._form_attr_cmd(attr=item, opr=opr)) elif w[attr]: if h and attr in h.keys(): h_ping = h.get(attr) or {} for item, value in iteritems(w[attr]): if ( opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)) ): commands.append( - self._form_attr_cmd( - attr=item, val=self._bool_to_str(value), opr=opr - ) + self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr) ) elif ( not opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)) ): commands.append(self._form_attr_cmd(attr=item, opr=opr)) return commands def _render_group(self, attr, w, h, opr): """ This function forms the commands for 'group' attribute based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_grp = {} if not opr and self._is_root_del(h, w, attr): commands.append(self._form_attr_cmd(attr=attr, opr=opr)) else: if h: h_grp = h.get("group") or {} if w: - commands.extend( - self._render_grp_mem("port_group", w["group"], h_grp, opr) - ) - commands.extend( - self._render_grp_mem( - "address_group", w["group"], h_grp, opr - ) - ) - commands.extend( - self._render_grp_mem( - "network_group", w["group"], h_grp, opr - ) - ) + commands.extend(self._render_grp_mem("port_group", w["group"], h_grp, opr)) + commands.extend(self._render_grp_mem("address_group", w["group"], h_grp, opr)) + commands.extend(self._render_grp_mem("network_group", w["group"], h_grp, opr)) return commands def _render_grp_mem(self, attr, w, h, opr): """ This function forms the commands for group list/members attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_grp = [] w_grp = [] l_set = ("name", "description") if w: w_grp = w.get(attr) or [] if h: h_grp = h.get(attr) or [] if w_grp: for want in w_grp: h = self.search_attrib_in_have(h_grp, want, "name") if "afi" in want and want["afi"] == "ipv6": - cmd = self._compute_command( - key="group", attr="ipv6-" + attr, opr=opr - ) + cmd = self._compute_command(key="group", attr="ipv6-" + attr, opr=opr) else: - cmd = self._compute_command( - key="group", attr=attr, opr=opr - ) + cmd = self._compute_command(key="group", attr=attr, opr=opr) for key, val in iteritems(want): if val: - if ( - opr - and key in l_set - and not (h and self._is_w_same(want, h, key)) - ): + if opr and key in l_set and not (h and self._is_w_same(want, h, key)): if key == "name": commands.append(cmd + " " + str(val)) else: commands.append( cmd + " " + want["name"] + " " + key + " '" + str(want[key]) + "'" ) elif not opr and key in l_set: - if key == "name" and self._is_grp_del( - h, want, key - ): + if key == "name" and self._is_grp_del(h, want, key): commands.append(cmd + " " + want["name"]) continue - if not ( - h and self._in_target(h, key) - ) and not self._is_grp_del(h, want, key): - commands.append( - cmd + " " + want["name"] + " " + key - ) + if not (h and self._in_target(h, key)) and not self._is_grp_del( + h, want, key + ): + commands.append(cmd + " " + want["name"] + " " + key) elif key == "members": commands.extend( - self._render_ports_addrs( - key, want, h, opr, cmd, want["name"], attr - ) + self._render_ports_addrs(key, want, h, opr, cmd, want["name"], attr) ) return commands def _render_ports_addrs(self, attr, w, h, opr, cmd, name, type): """ This function forms the commands for port/address/network group members based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param name: name of group. :param type: group type. :return: generated list of commands. """ commands = [] have = [] if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: if opr: members = list_diff_want_only(want, have) for member in members: commands.append( cmd + " " + name + " " + self._grp_type(type) + " " + member[self._get_mem_type(type)] ) elif not opr and have: members = list_diff_want_only(want, have) for member in members: commands.append( cmd + " " + name + " " + self._grp_type(type) + " " + member[self._get_mem_type(type)] ) return commands def _get_mem_type(self, group): """ This function returns the member type based on the type of group. """ return "port" if group == "port_group" else "address" def _render_state_policy(self, attr, w, h, opr): """ This function forms the commands for 'state-policy' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] have = [] l_set = ("log", "action", "connection_type") if not opr and self._is_root_del(h, w, attr): commands.append(self._form_attr_cmd(attr=attr, opr=opr)) else: w_sp = deepcopy(remove_empties(w)) want = w_sp.get(attr) or [] if h: have = h.get(attr) or [] if want: for w in want: h = self.search_attrib_in_have(have, w, "connection_type") for key, val in iteritems(w): if val and key != "connection_type": - if ( - opr - and key in l_set - and not (h and self._is_w_same(w, h, key)) - ): + if opr and key in l_set and not (h and self._is_w_same(w, h, key)): commands.append( self._form_attr_cmd( key=attr + " " + w["connection_type"], attr=key, val=self._bool_to_str(val), opr=opr, ) ) elif not opr and key in l_set: - if not ( - h and self._in_target(h, key) - ) and not self._is_del(l_set, h): + if not (h and self._in_target(h, key)) and not self._is_del( + l_set, h + ): if key == "action": commands.append( self._form_attr_cmd( - attr=attr - + " " - + w["connection_type"], + attr=attr + " " + w["connection_type"], opr=opr, ) ) else: commands.append( self._form_attr_cmd( - attr=attr - + " " - + w["connection_type"], + attr=attr + " " + w["connection_type"], val=self._bool_to_str(val), opr=opr, ) ) return commands def _render_route_redirects(self, attr, w, h, opr): """ This function forms the commands for 'route_redirects' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] have = [] l_set = ("afi", "ip_src_route") if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: for w in want: h = self.search_attrib_in_have(have, w, "afi") for key, val in iteritems(w): if val and key != "afi": - if ( - opr - and key in l_set - and not (h and self._is_w_same(w, h, key)) - ): + if opr and key in l_set and not (h and self._is_w_same(w, h, key)): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, ) ) elif not opr and key in l_set: if self._is_del(l_set, h): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, ) ) continue - if not ( - h and self._in_target(h, key) - ) and not self._is_del(l_set, h): + if not (h and self._in_target(h, key)) and not self._is_del(l_set, h): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, ) ) elif key == "icmp_redirects": - commands.extend( - self._render_icmp_redirects(key, w, h, opr) - ) + commands.extend(self._render_icmp_redirects(key, w, h, opr)) return commands def _render_icmp_redirects(self, attr, w, h, opr): """ This function forms the commands for 'icmp_redirects' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_red = {} l_set = ("send", "receive") if w[attr]: if h and attr in h.keys(): h_red = h.get(attr) or {} for item, value in iteritems(w[attr]): - if ( - opr - and item in l_set - and not (h_red and self._is_w_same(w[attr], h_red, item)) - ): + if opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)): commands.append( - self._form_attr_cmd( - attr=item, val=self._bool_to_str(value), opr=opr - ) + self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr) ) elif ( not opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)) ): commands.append(self._form_attr_cmd(attr=item, opr=opr)) return commands def search_attrib_in_have(self, have, want, attr): """ This function returns the attribute if it is present in target config. :param have: the target config. :param want: the desired config. :param attr: attribute name . :return: attribute/None """ if have: for h in have: if h[attr] == want[attr]: return h return None def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): """ This function forms the command for leaf attribute. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :return: generated command. """ - command = self._compute_command( - key=key, attr=self._map_attrib(attr), val=val, opr=opr - ) + command = self._compute_command(key=key, attr=self._map_attrib(attr), val=val, opr=opr) return command - def _compute_command( - self, key=None, attr=None, val=None, remove=False, opr=True - ): + def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): """ This function construct the add/delete command based on passed attributes. :param key: parent key. :param attr: attribute name :param value: value :param remove: True/False. :param opr: True/False. :return: generated command. """ if remove or not opr: cmd = "delete firewall " else: cmd = "set firewall " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val and opr: cmd += " '" + str(val) + "'" return cmd def _bool_to_str(self, val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ - return ( - "enable" - if str(val) == "True" - else "disable" - if str(val) == "False" - else val - ) + return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val def _grp_type(self, val): """ This function returns the group member type based on value argument. :param val: value. :return: member type. """ return ( - "address" - if val == "address_group" - else "network" - if val == "network_group" - else "port" + "address" if val == "address_group" else "network" if val == "network_group" else "port" ) def _is_w_same(self, w, h, key): """ This function checks whether the key value is same in desired and target config dictionary. :param w: base config. :param h: target config. :param key:attribute name. :return: True/False. """ return True if h and key in h and h[key] == w[key] else False def _in_target(self, h, key): """ This function checks whether the target exist and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h else False def _is_grp_del(self, w, h, key): """ This function checks whether group needed to be deleted based on desired and target configs. :param w: the desired config. :param h: the target config. :param key: group name. :return: True/False. """ - return ( - True - if h and key in h and (not w or key not in w or not w[key]) - else False - ) + return True if h and key in h and (not w or key not in w or not w[key]) else False def _is_root_del(self, w, h, key): """ This function checks whether a root attribute which can have further child attributes needed to be deleted. :param w: the desired config. :param h: the target config. :param key: attribute name. :return: True/False. """ - return ( - True - if h and key in h and (not w or key not in w or not w[key]) - else False - ) + return True if h and key in h and (not w or key not in w or not w[key]) else False def _is_del(self, b_set, h, key="number"): """ This function checks whether attribute needs to be deleted when operation is false and attribute present in present target config. :param b_set: attribute set. :param h: target config. :param key: number. :return: True/False. """ return key in b_set and not (h and self._in_target(h, key)) def _map_attrib(self, attrib, type=None): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "send": if type == "ipv6": regex = "ipv6-send-redirects" else: regex = "send-redirects" elif attrib == "ip_src_route": if type == "ipv6": regex = "ipv6-src-route" elif attrib == "receive": if type == "ipv6": regex = "ipv6-receive-redirects" else: regex = "receive-redirects" elif attrib == "disabled": regex = "disable" elif attrib == "all": regex = "all-ping" elif attrib == "broadcast": regex = "broadcast-ping" elif attrib == "validation": regex = "source-validation" return regex diff --git a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py index 45842f1..566bfeb 100644 --- a/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py +++ b/plugins/module_utils/network/vyos/config/firewall_interfaces/firewall_interfaces.py @@ -1,454 +1,411 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_firewall_interfaces class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, remove_empties, search_obj_in_list, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts class Firewall_interfaces(ConfigBase): """ The vyos_firewall_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "firewall_interfaces", ] def __init__(self, module): super(Firewall_interfaces, self).__init__(module) def get_firewall_interfaces_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - firewall_interfaces_facts = facts["ansible_network_resources"].get( - "firewall_interfaces" - ) + firewall_interfaces_facts = facts["ansible_network_resources"].get("firewall_interfaces") if not firewall_interfaces_facts: return [] return firewall_interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: - existing_firewall_interfaces_facts = ( - self.get_firewall_interfaces_facts() - ) + existing_firewall_interfaces_facts = self.get_firewall_interfaces_facts() else: existing_firewall_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": - commands.extend( - self.set_config(existing_firewall_interfaces_facts) - ) + commands.extend(self.set_config(existing_firewall_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": - changed_firewall_interfaces_facts = ( - self.get_firewall_interfaces_facts() - ) + changed_firewall_interfaces_facts = self.get_firewall_interfaces_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_firewall_interfaces_facts( - data=running_config - ) + result["parsed"] = self.get_firewall_interfaces_facts(data=running_config) else: changed_firewall_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_interfaces_facts if result["changed"]: result["after"] = changed_firewall_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_firewall_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_interfaces_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_firewall_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not w - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "overridden": commands.extend(self._state_overridden(w, h)) elif self.state == "deleted": commands.extend(self._state_deleted(w, h)) elif w: if self.state == "merged" or self.state == "rendered": commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for h in have: w = search_obj_in_list(h["name"], want) commands.extend(self._render_access_rules(h, w, opr=False)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for h_ar in have: w_ar = search_obj_in_list(h_ar["name"], want) if not w_ar and "access_rules" in h_ar: - commands.append( - self._compute_command(name=h_ar["name"], opr=False) - ) + commands.append(self._compute_command(name=h_ar["name"], opr=False)) else: h_rules = h_ar.get("access_rules") or [] key = "direction" if w_ar: w_rules = w_ar.get("access_rules") or [] if not w_rules and h_rules: - commands.append( - self._compute_command( - name=h_ar["name"], opr=False - ) - ) + commands.append(self._compute_command(name=h_ar["name"], opr=False)) if h_rules: for h_rule in h_rules: - w_rule = search_obj_in_list( - h_rule["afi"], w_rules, key="afi" - ) + w_rule = search_obj_in_list(h_rule["afi"], w_rules, key="afi") have_rules = h_rule.get("rules") or [] if w_rule: want_rules = w_rule.get("rules") or [] for h in have_rules: if key in h: - w = search_obj_in_list( - h[key], want_rules, key=key - ) + w = search_obj_in_list(h[key], want_rules, key=key) if ( not w or key not in w - or ( - "name" in h - and w - and "name" not in w - ) + or ("name" in h and w and "name" not in w) ): commands.append( self._compute_command( afi=h_rule["afi"], name=h_ar["name"], attrib=h[key], opr=False, ) ) commands.extend(self._state_merged(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] for w in want: h = search_obj_in_list(w["name"], have) commands.extend(self._render_access_rules(w, h)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: for w in want: h = search_obj_in_list(w["name"], have) if h and "access_rules" in h: commands.extend(self._delete_access_rules(w, h, opr=False)) elif have: for h in have: if "access_rules" in h: - commands.append( - self._compute_command(name=h["name"], opr=False) - ) + commands.append(self._compute_command(name=h["name"], opr=False)) return commands def _delete_access_rules(self, want, have, opr=False): """ This function forms the delete commands based on the 'opr' type for 'access_rules' attributes. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.get("access_rules") or [] if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.get("access_rules") or [] # if all firewall config needed to be deleted for specific interface # when operation is delete. if not w_rules and h_rules: commands.append(self._compute_command(name=want["name"], opr=opr)) if w_rules: for w in w_rules: h = search_obj_in_list(w["afi"], h_rules, key="afi") commands.extend(self._delete_rules(want["name"], w, h)) return commands def _delete_rules(self, name, want, have, opr=False): """ This function forms the delete commands based on the 'opr' type for rules attributes. :param name: interface id/name. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = [] key = "direction" w_rules = want.get("rules") or [] if have: h_rules = have.get("rules") or [] # when rule set needed to be removed on # (inbound|outbound|local interface) if h_rules and not w_rules: for h in h_rules: if key in h: commands.append( - self._compute_command( - afi=want["afi"], name=name, attrib=h[key], opr=opr - ) + self._compute_command(afi=want["afi"], name=name, attrib=h[key], opr=opr) ) for w in w_rules: h = search_obj_in_list(w[key], h_rules, key=key) if ( key in w and h and key in h and "name" in w and "name" in h and w["name"] == h["name"] ): commands.append( self._compute_command( afi=want["afi"], name=name, attrib=w[key], value=w["name"], opr=opr, ) ) return commands def _render_access_rules(self, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for 'access_rules' attributes. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.get("access_rules") or [] if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.get("access_rules") or [] if w_rules: for w in w_rules: h = search_obj_in_list(w["afi"], h_rules, key="afi") commands.extend(self._render_rules(want["name"], w, h, opr)) return commands def _render_rules(self, name, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rules attributes. :param name: interface id/name. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_rules = [] key = "direction" w_rules = want.get("rules") or [] if have: h_rules = have.get("rules") or [] for w in w_rules: h = search_obj_in_list(w[key], h_rules, key=key) if key in w: if opr: - if "name" in w and not ( - h and h[key] == w[key] and h["name"] == w["name"] - ): + if "name" in w and not (h and h[key] == w[key] and h["name"] == w["name"]): commands.append( self._compute_command( afi=want["afi"], name=name, attrib=w[key], value=w["name"], ) ) elif not (h and key in h): commands.append( - self._compute_command( - afi=want["afi"], name=name, attrib=w[key] - ) + self._compute_command(afi=want["afi"], name=name, attrib=w[key]) ) elif not opr: - if ( - not h - or key not in h - or ("name" in w and h and "name" not in h) - ): + if not h or key not in h or ("name" in w and h and "name" not in h): commands.append( self._compute_command( afi=want["afi"], name=name, attrib=w[key], opr=opr, ) ) return commands - def _compute_command( - self, afi=None, name=None, attrib=None, value=None, opr=True - ): + def _compute_command(self, afi=None, name=None, attrib=None, value=None, opr=True): """ This function construct the add/delete command based on passed attributes. :param afi: address type. :param name: interface name. :param attrib: attribute name. :param value: attribute value. :param opr: operation flag. :return: generated command. """ if not opr: cmd = "delete interfaces ethernet" + " " + name + " firewall" else: cmd = "set interfaces ethernet" + " " + name + " firewall" if attrib: cmd += " " + attrib if afi: cmd += " " + self._get_fw_type(afi) if value: cmd += " '" + str(value) + "'" return cmd def _get_fw_type(self, afi): """ This function returns the firewall rule-set type based on IP address. :param afi: address type :return: rule-set type. """ return "ipv6-name" if afi == "ipv6" else "name" diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py index ca47652..aa18a7e 100644 --- a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py @@ -1,1068 +1,859 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_firewall_rules class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type +import re from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, remove_empties, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( list_diff_want_only, ) -import re class Firewall_rules(ConfigBase): """ The vyos_firewall_rules class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "firewall_rules", ] def __init__(self, module): super(Firewall_rules, self).__init__(module) def get_firewall_rules_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - firewall_rules_facts = facts["ansible_network_resources"].get( - "firewall_rules" - ) + firewall_rules_facts = facts["ansible_network_resources"].get("firewall_rules") if not firewall_rules_facts: return [] return firewall_rules_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_firewall_rules_facts = self.get_firewall_rules_facts() else: existing_firewall_rules_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_firewall_rules_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_firewall_rules_facts = self.get_firewall_rules_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_firewall_rules_facts( - data=running_config - ) + result["parsed"] = self.get_firewall_rules_facts(data=running_config) else: changed_firewall_rules_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_rules_facts if result["changed"]: result["after"] = changed_firewall_rules_facts elif self.state == "gathered": result["gathered"] = changed_firewall_rules_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_rules_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_firewall_rules_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not w - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "overridden": commands.extend(self._state_overridden(w, h)) elif self.state == "deleted": commands.extend(self._state_deleted(w, h)) elif w: if self.state == "merged" or self.state == "rendered": commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: # Iterate over the afi rule sets we already have. for h in have: r_sets = self._get_r_sets(h) # Iterate over each rule set we already have. for rs in r_sets: # In the desired configuration, search for the rule set we # already have (to be replaced by our desired # configuration's rule set). wanted_rule_set = self.search_r_sets_in_have( want, rs["name"], "r_list", h["afi"] ) if wanted_rule_set is not None: # Remove the rules that we already have if the wanted # rules exist under the same name. commands.extend( self._add_r_sets( h["afi"], want=rs, have=wanted_rule_set, opr=False, ) ) # Merge the desired configuration into what we already have. commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for h in have: r_sets = self._get_r_sets(h) for rs in r_sets: - w = self.search_r_sets_in_have( - want, rs["name"], "r_list", h["afi"] - ) + w = self.search_r_sets_in_have(want, rs["name"], "r_list", h["afi"]) if not w: - commands.append( - self._compute_command( - h["afi"], rs["name"], remove=True - ) - ) + commands.append(self._compute_command(h["afi"], rs["name"], remove=True)) else: - commands.extend( - self._add_r_sets(h["afi"], rs, w, opr=False) - ) + commands.extend(self._add_r_sets(h["afi"], rs, w, opr=False)) commands.extend(self._state_merged(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] for w in want: r_sets = self._get_r_sets(w) for rs in r_sets: - h = self.search_r_sets_in_have( - have, rs["name"], "r_list", w["afi"] - ) + h = self.search_r_sets_in_have(have, rs["name"], "r_list", w["afi"]) commands.extend(self._add_r_sets(w["afi"], rs, h)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: for w in want: r_sets = self._get_r_sets(w) if r_sets: for rs in r_sets: - h = self.search_r_sets_in_have( - have, rs["name"], "r_list", w["afi"] - ) + h = self.search_r_sets_in_have(have, rs["name"], "r_list", w["afi"]) if h: - commands.append( - self._compute_command( - w["afi"], h["name"], remove=True - ) - ) + commands.append(self._compute_command(w["afi"], h["name"], remove=True)) elif have: for h in have: if h["afi"] == w["afi"]: - commands.append( - self._compute_command(w["afi"], remove=True) - ) + commands.append(self._compute_command(w["afi"], remove=True)) elif have: for h in have: r_sets = self._get_r_sets(h) if r_sets: - commands.append( - self._compute_command(afi=h["afi"], remove=True) - ) + commands.append(self._compute_command(afi=h["afi"], remove=True)) return commands def _add_r_sets(self, afi, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rule-sets attributes. :param afi: address type. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] l_set = ("description", "default_action", "enable_default_log") h_rs = {} h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.pop("rules", None) if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.pop("rules", None) if w_rs: for key, val in iteritems(w_rs): - if ( - opr - and key in l_set - and not (h_rs and self._is_w_same(w_rs, h_rs, key)) - ): + if opr and key in l_set and not (h_rs and self._is_w_same(w_rs, h_rs, key)): if key == "enable_default_log": - if val and ( - not h_rs or key not in h_rs or not h_rs[key] - ): - commands.append( - self._add_rs_base_attrib( - afi, want["name"], key, w_rs - ) - ) + if val and (not h_rs or key not in h_rs or not h_rs[key]): + commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs)) else: - commands.append( - self._add_rs_base_attrib( - afi, want["name"], key, w_rs - ) - ) + commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs)) elif not opr and key in l_set: if ( key == "enable_default_log" and val and h_rs and (key not in h_rs or not h_rs[key]) ): - commands.append( - self._add_rs_base_attrib( - afi, want["name"], key, w_rs, opr - ) - ) + commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs, opr)) elif not (h_rs and self._in_target(h_rs, key)): - commands.append( - self._add_rs_base_attrib( - afi, want["name"], key, w_rs, opr - ) - ) - commands.extend( - self._add_rules(afi, want["name"], w_rules, h_rules, opr) - ) + commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs, opr)) + commands.extend(self._add_rules(afi, want["name"], w_rules, h_rules, opr)) if h_rules: have["rules"] = h_rules if w_rules: want["rules"] = w_rules return commands def _add_rules(self, afi, name, w_rules, h_rules, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rules attributes. :param want: desired config. :param have: target config. :return: generated commands list. """ commands = [] l_set = ( "ipsec", "action", "number", "protocol", "fragment", "disable", "description", "log", ) if w_rules: for w in w_rules: cmd = self._compute_command(afi, name, w["number"], opr=opr) - h = self.search_r_sets_in_have( - h_rules, w["number"], type="rules" - ) + h = self.search_r_sets_in_have(h_rules, w["number"], type="rules") for key, val in iteritems(w): if val: - if ( - opr - and key in l_set - and not (h and self._is_w_same(w, h, key)) - ): + if opr and key in l_set and not (h and self._is_w_same(w, h, key)): if key == "disable": - if not ( - not val - and (not h or key not in h or not h[key]) - ): - commands.append( - self._add_r_base_attrib( - afi, name, key, w - ) - ) + if not (not val and (not h or key not in h or not h[key])): + commands.append(self._add_r_base_attrib(afi, name, key, w)) else: - commands.append( - self._add_r_base_attrib(afi, name, key, w) - ) + commands.append(self._add_r_base_attrib(afi, name, key, w)) elif not opr: if key == "number" and self._is_del(l_set, h): - commands.append( - self._add_r_base_attrib( - afi, name, key, w, opr=opr - ) - ) + commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr)) continue - if ( - key == "disable" - and val - and h - and (key not in h or not h[key]) - ): - commands.append( - self._add_r_base_attrib( - afi, name, key, w, opr=opr - ) - ) + if key == "disable" and val and h and (key not in h or not h[key]): + commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr)) elif ( key in l_set and not (h and self._in_target(h, key)) and not self._is_del(l_set, h) ): - commands.append( - self._add_r_base_attrib( - afi, name, key, w, opr=opr - ) - ) + commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr)) elif key == "p2p": commands.extend(self._add_p2p(key, w, h, cmd, opr)) elif key == "tcp": commands.extend(self._add_tcp(key, w, h, cmd, opr)) elif key == "time": - commands.extend( - self._add_time(key, w, h, cmd, opr) - ) + commands.extend(self._add_time(key, w, h, cmd, opr)) elif key == "icmp": - commands.extend( - self._add_icmp(key, w, h, cmd, opr) - ) + commands.extend(self._add_icmp(key, w, h, cmd, opr)) elif key == "state": - commands.extend( - self._add_state(key, w, h, cmd, opr) - ) + commands.extend(self._add_state(key, w, h, cmd, opr)) elif key == "limit": - commands.extend( - self._add_limit(key, w, h, cmd, opr) - ) + commands.extend(self._add_limit(key, w, h, cmd, opr)) elif key == "recent": - commands.extend( - self._add_recent(key, w, h, cmd, opr) - ) + commands.extend(self._add_recent(key, w, h, cmd, opr)) elif key == "destination" or key == "source": - commands.extend( - self._add_src_or_dest(key, w, h, cmd, opr) - ) + commands.extend(self._add_src_or_dest(key, w, h, cmd, opr)) return commands def _add_p2p(self, attr, w, h, cmd, opr): """ This function forms the set/delete commands based on the 'opr' type for p2p applications attributes. :param want: desired config. :param have: target config. :return: generated commands list. """ commands = [] have = [] if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: if opr: applications = list_diff_want_only(want, have) for app in applications: - commands.append( - cmd + (" " + attr + " " + app["application"]) - ) + commands.append(cmd + (" " + attr + " " + app["application"])) elif not opr and have: applications = list_diff_want_only(want, have) for app in applications: - commands.append( - cmd + (" " + attr + " " + app["application"]) - ) + commands.append(cmd + (" " + attr + " " + app["application"])) return commands def _add_state(self, attr, w, h, cmd, opr): """ This function forms the command for 'state' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_state = {} commands = [] l_set = ("new", "invalid", "related", "established") if w[attr]: if h and attr in h.keys(): h_state = h.get(attr) or {} for item, val in iteritems(w[attr]): if ( opr and item in l_set - and not ( - h_state and self._is_w_same(w[attr], h_state, item) - ) - ): - commands.append( - cmd - + ( - " " - + attr - + " " - + item - + " " - + self._bool_to_str(val) - ) - ) - elif ( - not opr - and item in l_set - and not (h_state and self._in_target(h_state, item)) + and not (h_state and self._is_w_same(w[attr], h_state, item)) ): + commands.append(cmd + (" " + attr + " " + item + " " + self._bool_to_str(val))) + elif not opr and item in l_set and not (h_state and self._in_target(h_state, item)): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_recent(self, attr, w, h, cmd, opr): """ This function forms the command for 'recent' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_recent = {} l_set = ("count", "time") if w[attr]: if h and attr in h.keys(): h_recent = h.get(attr) or {} for item, val in iteritems(w[attr]): if ( opr and item in l_set - and not ( - h_recent and self._is_w_same(w[attr], h_recent, item) - ) + and not (h_recent and self._is_w_same(w[attr], h_recent, item)) ): - commands.append( - cmd + (" " + attr + " " + item + " " + str(val)) - ) + commands.append(cmd + (" " + attr + " " + item + " " + str(val))) elif ( - not opr - and item in l_set - and not (h_recent and self._in_target(h_recent, item)) + not opr and item in l_set and not (h_recent and self._in_target(h_recent, item)) ): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_icmp(self, attr, w, h, cmd, opr): """ This function forms the commands for 'icmp' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_icmp = {} l_set = ("code", "type", "type_name") if w[attr]: if h and attr in h.keys(): h_icmp = h.get(attr) or {} for item, val in iteritems(w[attr]): if ( opr and item in l_set and not (h_icmp and self._is_w_same(w[attr], h_icmp, item)) ): if item == "type_name": os_version = self._get_os_version() ver = re.search( "vyos ([\\d\\.]+)-?.*", # noqa: W605 os_version, re.IGNORECASE, ) if ver.group(1) >= "1.4": param_name = "type-name" else: param_name = "type" if "ipv6-name" in cmd: - commands.append( - cmd - + ( - " " - + "icmpv6" - + " " - + param_name - + " " - + val - ) - ) + commands.append(cmd + (" " + "icmpv6" + " " + param_name + " " + val)) else: commands.append( - cmd - + ( - " " - + attr - + " " - + item.replace("_", "-") - + " " - + val - ) + cmd + (" " + attr + " " + item.replace("_", "-") + " " + val) ) else: - commands.append( - cmd + (" " + attr + " " + item + " " + str(val)) - ) - elif ( - not opr - and item in l_set - and not (h_icmp and self._in_target(h_icmp, item)) - ): + commands.append(cmd + (" " + attr + " " + item + " " + str(val))) + elif not opr and item in l_set and not (h_icmp and self._in_target(h_icmp, item)): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_time(self, attr, w, h, cmd, opr): """ This function forms the commands for 'time' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_time = {} l_set = ( "utc", "stopdate", "stoptime", "weekdays", "monthdays", "startdate", "starttime", ) if w[attr]: if h and attr in h.keys(): h_time = h.get(attr) or {} for item, val in iteritems(w[attr]): if ( opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)) ): if item == "utc": - if not ( - not val and (not h_time or item not in h_time) - ): + if not (not val and (not h_time or item not in h_time)): commands.append(cmd + (" " + attr + " " + item)) else: - commands.append( - cmd + (" " + attr + " " + item + " " + val) - ) + commands.append(cmd + (" " + attr + " " + item + " " + val)) elif ( not opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)) ): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_tcp(self, attr, w, h, cmd, opr): """ This function forms the commands for 'tcp' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_tcp = {} commands = [] if w[attr]: key = "flags" flags = w[attr].get(key) or {} if flags: if h and key in h[attr].keys(): h_tcp = h[attr].get(key) or {} if flags: - if opr and not ( - h_tcp and self._is_w_same(w[attr], h[attr], key) - ): - commands.append( - cmd + (" " + attr + " " + key + " " + flags) - ) - if not opr and not ( - h_tcp and self._is_w_same(w[attr], h[attr], key) - ): - commands.append( - cmd + (" " + attr + " " + key + " " + flags) - ) + if opr and not (h_tcp and self._is_w_same(w[attr], h[attr], key)): + commands.append(cmd + (" " + attr + " " + key + " " + flags)) + if not opr and not (h_tcp and self._is_w_same(w[attr], h[attr], key)): + commands.append(cmd + (" " + attr + " " + key + " " + flags)) return commands def _add_limit(self, attr, w, h, cmd, opr): """ This function forms the commands for 'limit' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_limit = {} commands = [] if w[attr]: key = "burst" if ( opr and key in w[attr].keys() - and not ( - h - and attr in h.keys() - and self._is_w_same(w[attr], h[attr], key) - ) + and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)) ): - commands.append( - cmd - + (" " + attr + " " + key + " " + str(w[attr].get(key))) - ) + commands.append(cmd + (" " + attr + " " + key + " " + str(w[attr].get(key)))) elif ( not opr and key in w[attr].keys() - and not ( - h and attr in h.keys() and self._in_target(h[attr], key) - ) + and not (h and attr in h.keys() and self._in_target(h[attr], key)) ): - commands.append( - cmd - + (" " + attr + " " + key + " " + str(w[attr].get(key))) - ) + commands.append(cmd + (" " + attr + " " + key + " " + str(w[attr].get(key)))) key = "rate" rate = w[attr].get(key) or {} if rate: if h and key in h[attr].keys(): h_limit = h[attr].get(key) or {} if "unit" in rate and "number" in rate: if opr and not ( h_limit and self._is_w_same(rate, h_limit, "unit") and self.is_w_same(rate, h_limit, "number") ): commands.append( cmd + ( " " + attr + " " + key + " " + str(rate["number"]) + "/" + rate["unit"] ) ) if not opr and not ( h_limit and self._is_w_same(rate, h_limit, "unit") and self._is_w_same(rate, h_limit, "number") ): commands.append(cmd + (" " + attr + " " + key)) return commands def _add_src_or_dest(self, attr, w, h, cmd, opr=True): """ This function forms the commands for 'src/dest' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_group = {} g_set = ("port_group", "address_group", "network_group") if w[attr]: keys = ("address", "mac_address", "port") for key in keys: if ( opr and key in w[attr].keys() - and not ( - h - and attr in h.keys() - and self._is_w_same(w[attr], h[attr], key) - ) + and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)) ): commands.append( - cmd - + ( - " " - + attr - + " " - + key.replace("_", "-") - + " " - + w[attr].get(key) - ) + cmd + (" " + attr + " " + key.replace("_", "-") + " " + w[attr].get(key)) ) elif ( not opr and key in w[attr].keys() - and not ( - h - and attr in h.keys() - and self._in_target(h[attr], key) - ) + and not (h and attr in h.keys() and self._in_target(h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key)) key = "group" group = w[attr].get(key) or {} if group: h_group = {} if h and h.get(attr) and key in h[attr].keys(): h_group = h[attr].get(key) for item, val in iteritems(group): if val: if ( opr and item in g_set - and not ( - h_group - and self._is_w_same(group, h_group, item) - ) + and not (h_group and self._is_w_same(group, h_group, item)) ): commands.append( cmd + ( " " + attr + " " + key + " " + item.replace("_", "-") + " " + val ) ) elif ( not opr and item in g_set - and not ( - h_group and self._in_target(h_group, item) - ) + and not (h_group and self._in_target(h_group, item)) ): commands.append( - cmd - + ( - " " - + attr - + " " - + key - + " " - + item.replace("_", "-") - ) + cmd + (" " + attr + " " + key + " " + item.replace("_", "-")) ) return commands def search_r_sets_in_have(self, have, w_name, type="rule_sets", afi=None): """ This function returns the rule-set/rule if it is present in target config. :param have: target config. :param w_name: rule-set name. :param type: rule_sets/rule/r_list. :param afi: address family (when type is r_list). :return: rule-set/rule. """ if have: key = "name" if type == "rules": key = "number" for r in have: if r[key] == w_name: return r elif type == "r_list": for h in have: if h["afi"] == afi: r_sets = self._get_r_sets(h) for rs in r_sets: if rs[key] == w_name: return rs else: for rs in have: if rs[key] == w_name: return rs return None def _get_r_sets(self, item, type="rule_sets"): """ This function returns the list of rule-sets/rules. :param item: config dictionary. :param type: rule_sets/rule/r_list. :return: list of rule-sets/rules. """ rs_list = [] r_sets = item[type] if r_sets: for rs in r_sets: rs_list.append(rs) return rs_list def _compute_command( self, afi, name=None, number=None, attrib=None, value=None, remove=False, opr=True, ): """ This function construct the add/delete command based on passed attributes. :param afi: address type. :param name: rule-set name. :param number: rule-number. :param attrib: attribute name. :param value: value. :param remove: True if delete command needed to be construct. :param opr: opeeration flag. :return: generated command. """ if remove or not opr: cmd = "delete firewall " + self._get_fw_type(afi) else: cmd = "set firewall " + self._get_fw_type(afi) if name: cmd += " " + name if number: cmd += " rule " + str(number) if attrib: cmd += " " + attrib.replace("_", "-") - if ( - value - and opr - and attrib != "enable_default_log" - and attrib != "disable" - ): + if value and opr and attrib != "enable_default_log" and attrib != "disable": cmd += " '" + str(value) + "'" return cmd def _add_r_base_attrib(self, afi, name, attr, rule, opr=True): """ This function forms the command for 'rules' attributes which doesn't have further sub attributes. :param afi: address type. :param name: rule-set name :param attrib: attribute name :param rule: rule config dictionary. :param opr: True/False. :return: generated command. """ if attr == "number": - command = self._compute_command( - afi=afi, name=name, number=rule["number"], opr=opr - ) + command = self._compute_command(afi=afi, name=name, number=rule["number"], opr=opr) else: command = self._compute_command( afi=afi, name=name, number=rule["number"], attrib=attr, value=rule[attr], opr=opr, ) return command def _add_rs_base_attrib(self, afi, name, attrib, rule, opr=True): """ This function forms the command for 'rule-sets' attributes which doesn't have further sub attributes. :param afi: address type. :param name: rule-set name :param attrib: attribute name :param rule: rule config dictionary. :param opr: True/False. :return: generated command. """ command = self._compute_command( afi=afi, name=name, attrib=attrib, value=rule[attrib], opr=opr ) return command def _bool_to_str(self, val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ return "enable" if val else "disable" def _get_fw_type(self, afi): """ This function returns the firewall rule-set type based on IP address. :param afi: address type :return: rule-set type. """ return "ipv6-name" if afi == "ipv6" else "name" def _is_del(self, l_set, h, key="number"): """ This function checks whether rule needs to be deleted based on the rule number. :param l_set: attribute set. :param h: target config. :param key: number. :return: True/False. """ return key in l_set and not (h and self._in_target(h, key)) def _is_w_same(self, w, h, key): """ This function checks whether the key value is same in base and target config dictionary. :param w: base config. :param h: target config. :param key:attribute name. :return: True/False. """ return True if h and key in h and h[key] == w[key] else False def _in_target(self, h, key): """ This function checks whether the target nexist and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h else False def _is_base_attrib(self, key): """ This function checks whether key is present in predefined based attribute set. :param key: :return: True/False. """ r_set = ( "p2p", "ipsec", "log", "action", "fragment", "protocol", "disable", "description", "mac_address", "default_action", "enable_default_log", ) return True if key in r_set else False def _get_os_version(self): os_version = "1.2" if self._connection: - os_version = self._connection.get_device_info()[ - "network_os_version" - ] + os_version = self._connection.get_device_info()["network_os_version"] return os_version diff --git a/plugins/module_utils/network/vyos/config/hostname/hostname.py b/plugins/module_utils/network/vyos/config/hostname/hostname.py index cf2c8c2..8b30a69 100644 --- a/plugins/module_utils/network/vyos/config/hostname/hostname.py +++ b/plugins/module_utils/network/vyos/config/hostname/hostname.py @@ -1,75 +1,74 @@ # # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_hostname config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.hostname import ( HostnameTemplate, ) class Hostname(ResourceModule): """ The vyos_hostname config class """ def __init__(self, module): super(Hostname, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="hostname", tmplt=HostnameTemplate(), ) self.parsers = [] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self.want haved = self.have if self.state == "deleted": wantd = {} self._compare(want=wantd, have=haved) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Hostname network resource. """ self.compare(parsers="hostname", want=want, have=have) diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py index 484e600..852db46 100644 --- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py @@ -1,354 +1,331 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_interfaces class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, dict_diff, remove_empties, + to_list, ) -from ansible.module_utils.six import iteritems -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - search_obj_in_list, - get_interface_type, dict_delete, + get_interface_type, + search_obj_in_list, ) class Interfaces(ConfigBase): """ The vyos_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = ["interfaces"] def __init__(self, module): super(Interfaces, self).__init__(module) def get_interfaces_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) interfaces_facts = facts["ansible_network_resources"].get("interfaces") if not interfaces_facts: return [] return interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} commands = list() warnings = list() if self.state in self.ACTION_STATES: existing_interfaces_facts = self.get_interfaces_facts() else: existing_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_interfaces_facts = self.get_interfaces_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) result["parsed"] = self.get_interfaces_facts(data=running_config) else: changed_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_interfaces_facts if result["changed"]: result["after"] = changed_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_interfaces_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not want - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "overridden": commands.extend(self._state_overridden(want=want, have=have)) elif self.state == "deleted": if not want: for intf in have: - commands.extend( - self._state_deleted({"name": intf["name"]}, intf) - ) + commands.extend(self._state_deleted({"name": intf["name"]}, intf)) else: for item in want: obj_in_have = search_obj_in_list(item["name"], have) commands.extend(self._state_deleted(item, obj_in_have)) else: for item in want: name = item["name"] enable_state = item["enabled"] obj_in_have = search_obj_in_list(name, have) if not obj_in_have: obj_in_have = {"name": name, "enabled": enable_state} if self.state in ("merged", "rendered"): commands.extend(self._state_merged(item, obj_in_have)) elif self.state == "replaced": commands.extend(self._state_replaced(item, obj_in_have)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for intf in have: intf_in_want = search_obj_in_list(intf["name"], want) if not intf_in_want: - commands.extend( - self._state_deleted({"name": intf["name"]}, intf) - ) + commands.extend(self._state_deleted({"name": intf["name"]}, intf)) for intf in want: intf_in_have = search_obj_in_list(intf["name"], have) if not intf_in_have: intf_in_have = { "name": intf["name"], "enabled": intf["enabled"], } commands.extend(self._state_replaced(intf, intf_in_have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) updates = dict_diff(have_copy, want_copy) if updates: for key, value in iteritems(updates): commands.append( - self._compute_commands( - key=key, value=value, interface=want_copy["name"] - ) + self._compute_commands(key=key, value=value, interface=want_copy["name"]) ) if want_vifs: for want_vif in want_vifs: - have_vif = search_obj_in_list( - want_vif["vlan_id"], have_vifs, key="vlan_id" - ) + have_vif = search_obj_in_list(want_vif["vlan_id"], have_vifs, key="vlan_id") if not have_vif: have_vif = { "vlan_id": want_vif["vlan_id"], "enabled": True, } vif_updates = dict_diff(have_vif, want_vif) if vif_updates: for key, value in iteritems(vif_updates): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], vif=want_vif["vlan_id"], ) ) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) for key in dict_delete(have_copy, want_copy).keys(): if key == "enabled": continue commands.append( - self._compute_commands( - key=key, interface=want_copy["name"], remove=True - ) + self._compute_commands(key=key, interface=want_copy["name"], remove=True) ) if have_copy["enabled"] is False: commands.append( - self._compute_commands( - key="enabled", value=True, interface=want_copy["name"] - ) + self._compute_commands(key="enabled", value=True, interface=want_copy["name"]) ) if have_vifs: for have_vif in have_vifs: - want_vif = search_obj_in_list( - have_vif["vlan_id"], want_vifs, key="vlan_id" - ) + want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") if not want_vif: want_vif = { "vlan_id": have_vif["vlan_id"], "enabled": True, } for key in dict_delete(have_vif, want_vif).keys(): if key == "enabled": continue commands.append( self._compute_commands( key=key, interface=want_copy["name"], vif=want_vif["vlan_id"], remove=True, ) ) if have_vif["enabled"] is False: commands.append( self._compute_commands( key="enabled", value=True, interface=want_copy["name"], vif=want_vif["vlan_id"], ) ) return commands - def _compute_commands( - self, interface, key, vif=None, value=None, remove=False - ): - intf_context = "interfaces {0} {1}".format( - get_interface_type(interface), interface - ) + def _compute_commands(self, interface, key, vif=None, value=None, remove=False): + intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) set_cmd = "set {0}".format(intf_context) del_cmd = "delete {0}".format(intf_context) if vif: set_cmd = set_cmd + (" vif {0}".format(vif)) del_cmd = del_cmd + (" vif {0}".format(vif)) if key == "enabled": if not value: command = "{0} disable".format(set_cmd) else: command = "{0} disable".format(del_cmd) else: if not remove: command = "{0} {1} '{2}'".format(set_cmd, key, value) else: command = "{0} {1}".format(del_cmd, key) return command diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py index 6f143be..61dd1f4 100644 --- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py @@ -1,344 +1,317 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_l3_interfaces class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, remove_empties, + to_list, ) -from ansible.module_utils.six import iteritems -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - search_obj_in_list, - get_interface_type, diff_list_of_dicts, + get_interface_type, + search_obj_in_list, ) class L3_interfaces(ConfigBase): """ The vyos_l3_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "l3_interfaces", ] def __init__(self, module): super(L3_interfaces, self).__init__(module) def get_l3_interfaces_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - l3_interfaces_facts = facts["ansible_network_resources"].get( - "l3_interfaces" - ) + l3_interfaces_facts = facts["ansible_network_resources"].get("l3_interfaces") if not l3_interfaces_facts: return [] return l3_interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_l3_interfaces_facts = self.get_l3_interfaces_facts() else: existing_l3_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_l3_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_l3_interfaces_facts = self.get_l3_interfaces_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_l3_interfaces_facts( - data=running_config - ) + result["parsed"] = self.get_l3_interfaces_facts(data=running_config) else: changed_l3_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_l3_interfaces_facts if result["changed"]: result["after"] = changed_l3_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_l3_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_l3_interfaces_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_l3_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] state = self._module.params["state"] - if ( - state in ("merged", "replaced", "overridden", "rendered") - and not want - ): + if state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - state - ) + msg="value of config parameter must not be empty for state {0}".format(state) ) if state == "overridden": commands.extend(self._state_overridden(want=want, have=have)) elif state == "deleted": if not want: for intf in have: - commands.extend( - self._state_deleted({"name": intf["name"]}, intf) - ) + commands.extend(self._state_deleted({"name": intf["name"]}, intf)) else: for item in want: obj_in_have = search_obj_in_list(item["name"], have) commands.extend(self._state_deleted(item, obj_in_have)) else: for item in want: name = item["name"] obj_in_have = search_obj_in_list(name, have) if not obj_in_have: obj_in_have = {"name": item["name"]} if state in ("merged", "rendered"): commands.extend(self._state_merged(item, obj_in_have)) elif state == "replaced": commands.extend(self._state_replaced(item, obj_in_have)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for intf in have: intf_in_want = search_obj_in_list(intf["name"], want) if not intf_in_want: - commands.extend( - self._state_deleted({"name": intf["name"]}, intf) - ) + commands.extend(self._state_deleted({"name": intf["name"]}, intf)) for intf in want: intf_in_have = search_obj_in_list(intf["name"], have) commands.extend(self._state_replaced(intf, intf_in_have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) for update in self._get_updates(want_copy, have_copy): for key, value in iteritems(update): commands.append( - self._compute_commands( - key=key, value=value, interface=want_copy["name"] - ) + self._compute_commands(key=key, value=value, interface=want_copy["name"]) ) if want_vifs: for want_vif in want_vifs: - have_vif = search_obj_in_list( - want_vif["vlan_id"], have_vifs, key="vlan_id" - ) + have_vif = search_obj_in_list(want_vif["vlan_id"], have_vifs, key="vlan_id") if not have_vif: have_vif = {} for update in self._get_updates(want_vif, have_vif): for key, value in iteritems(update): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], vif=want_vif["vlan_id"], ) ) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) for update in self._get_updates(have_copy, want_copy): for key, value in iteritems(update): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], remove=True, ) ) if have_vifs: for have_vif in have_vifs: - want_vif = search_obj_in_list( - have_vif["vlan_id"], want_vifs, key="vlan_id" - ) + want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") if not want_vif: want_vif = {"vlan_id": have_vif["vlan_id"]} for update in self._get_updates(have_vif, want_vif): for key, value in iteritems(update): commands.append( self._compute_commands( key=key, interface=want_copy["name"], value=value, vif=want_vif["vlan_id"], remove=True, ) ) return commands - def _compute_commands( - self, interface, key, vif=None, value=None, remove=False - ): - intf_context = "interfaces {0} {1}".format( - get_interface_type(interface), interface - ) + def _compute_commands(self, interface, key, vif=None, value=None, remove=False): + intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) set_cmd = "set {0}".format(intf_context) del_cmd = "delete {0}".format(intf_context) if vif: set_cmd = set_cmd + (" vif {0}".format(vif)) del_cmd = del_cmd + (" vif {0}".format(vif)) if remove: command = "{0} {1} '{2}'".format(del_cmd, key, value) else: command = "{0} {1} '{2}'".format(set_cmd, key, value) return command def _get_updates(self, want, have): updates = [] - updates = diff_list_of_dicts( - want.get("ipv4", []), have.get("ipv4", []) - ) - updates.extend( - diff_list_of_dicts(want.get("ipv6", []), have.get("ipv6", [])) - ) + updates = diff_list_of_dicts(want.get("ipv4", []), have.get("ipv4", [])) + updates.extend(diff_list_of_dicts(want.get("ipv6", []), have.get("ipv6", []))) return updates diff --git a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py index 7e7b39a..314ff93 100644 --- a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py @@ -1,468 +1,432 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_lag_interfaces class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, dict_diff, + to_list, ) -from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - search_obj_in_list, get_lst_diff_for_dicts, - list_diff_want_only, list_diff_have_only, + list_diff_want_only, + search_obj_in_list, ) class Lag_interfaces(ConfigBase): """ The vyos_lag_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lag_interfaces", ] params = [ "arp_monitor", "hash_policy", "members", "mode", "name", "primary", ] def __init__(self, module): super(Lag_interfaces, self).__init__(module) def get_lag_interfaces_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - lag_interfaces_facts = facts["ansible_network_resources"].get( - "lag_interfaces" - ) + lag_interfaces_facts = facts["ansible_network_resources"].get("lag_interfaces") if not lag_interfaces_facts: return [] return lag_interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_lag_interfaces_facts = self.get_lag_interfaces_facts() else: existing_lag_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lag_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_lag_interfaces_facts = self.get_lag_interfaces_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_lag_interfaces_facts( - data=running_config - ) + result["parsed"] = self.get_lag_interfaces_facts(data=running_config) else: changed_lag_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lag_interfaces_facts if result["changed"]: result["after"] = changed_lag_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_lag_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_lag_interfaces_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_lag_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not want - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "overridden": commands.extend(self._state_overridden(want, have)) elif self.state == "deleted": if want: for want_item in want: name = want_item["name"] obj_in_have = search_obj_in_list(name, have) commands.extend(self._state_deleted(obj_in_have)) else: for have_item in have: commands.extend(self._state_deleted(have_item)) else: for want_item in want: name = want_item["name"] obj_in_have = search_obj_in_list(name, have) if self.state in ("merged", "rendered"): commands.extend(self._state_merged(want_item, obj_in_have)) elif self.state == "replaced": - commands.extend( - self._state_replaced(want_item, obj_in_have) - ) + commands.extend(self._state_replaced(want_item, obj_in_have)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._render_del_commands(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for have_item in have: lag_name = have_item["name"] obj_in_want = search_obj_in_list(lag_name, want) if not obj_in_want: commands.extend(self._purge_attribs(have_item)) for want_item in want: name = want_item["name"] obj_in_have = search_obj_in_list(name, have) commands.extend(self._state_replaced(want_item, obj_in_have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] if have: commands.extend(self._render_updates(want, have)) else: commands.extend(self._render_set_commands(want)) return commands def _state_deleted(self, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if have: commands.extend(self._purge_attribs(have)) return commands def _render_updates(self, want, have): commands = [] temp_have_members = have.pop("members", None) temp_want_members = want.pop("members", None) updates = dict_diff(have, want) if temp_have_members: have["members"] = temp_have_members if temp_want_members: want["members"] = temp_want_members commands.extend(self._add_bond_members(want, have)) if updates: for key, value in iteritems(updates): if value: if key == "arp_monitor": - commands.extend( - self._add_arp_monitor(updates, key, want, have) - ) + commands.extend(self._add_arp_monitor(updates, key, want, have)) else: - commands.append( - self._compute_command( - have["name"], key, str(value) - ) - ) + commands.append(self._compute_command(have["name"], key, str(value))) return commands def _render_set_commands(self, want): commands = [] have = [] params = Lag_interfaces.params for attrib in params: value = want[attrib] if value: if attrib == "arp_monitor": - commands.extend( - self._add_arp_monitor(want, attrib, want, have) - ) + commands.extend(self._add_arp_monitor(want, attrib, want, have)) elif attrib == "members": commands.extend(self._add_bond_members(want, have)) elif attrib != "name": - commands.append( - self._compute_command( - want["name"], attrib, value=str(value) - ) - ) + commands.append(self._compute_command(want["name"], attrib, value=str(value))) return commands def _purge_attribs(self, have): commands = [] for item in Lag_interfaces.params: if have.get(item): if item == "members": commands.extend(self._delete_bond_members(have)) elif item != "name": - commands.append( - self._compute_command( - have["name"], attrib=item, remove=True - ) - ) + commands.append(self._compute_command(have["name"], attrib=item, remove=True)) return commands def _render_del_commands(self, want, have): commands = [] params = Lag_interfaces.params for attrib in params: if attrib == "members": commands.extend(self._update_bond_members(attrib, want, have)) elif attrib == "arp_monitor": commands.extend(self._update_arp_monitor(attrib, want, have)) elif have.get(attrib) and not want.get(attrib): - commands.append( - self._compute_command(have["name"], attrib, remove=True) - ) + commands.append(self._compute_command(have["name"], attrib, remove=True)) return commands def _add_bond_members(self, want, have): commands = [] diff_members = get_lst_diff_for_dicts(want, have, "members") if diff_members: for key in diff_members: commands.append( self._compute_command( key["member"], "bond-group", want["name"], type="ethernet", ) ) return commands def _add_arp_monitor(self, updates, key, want, have): commands = [] arp_monitor = updates.get(key) or {} - diff_targets = self._get_arp_monitor_target_diff( - want, have, key, "target" - ) + diff_targets = self._get_arp_monitor_target_diff(want, have, key, "target") if "interval" in arp_monitor: commands.append( self._compute_command( key=want["name"] + " arp-monitor", attrib="interval", value=str(arp_monitor["interval"]), ) ) if diff_targets: for target in diff_targets: commands.append( self._compute_command( key=want["name"] + " arp-monitor", attrib="target", value=target, ) ) return commands def _delete_bond_members(self, have): commands = [] for member in have["members"]: commands.append( self._compute_command( member["member"], "bond-group", have["name"], remove=True, type="ethernet", ) ) return commands def _update_arp_monitor(self, key, want, have): commands = [] want_arp_target = [] have_arp_target = [] want_arp_monitor = want.get(key) or {} have_arp_monitor = have.get(key) or {} if want_arp_monitor and "target" in want_arp_monitor: want_arp_target = want_arp_monitor["target"] if have_arp_monitor and "target" in have_arp_monitor: have_arp_target = have_arp_monitor["target"] if "interval" in have_arp_monitor and not want_arp_monitor: commands.append( self._compute_command( key=have["name"] + " arp-monitor", attrib="interval", remove=True, ) ) if "target" in have_arp_monitor: target_diff = list_diff_have_only(want_arp_target, have_arp_target) if target_diff: for target in target_diff: commands.append( self._compute_command( key=have["name"] + " arp-monitor", attrib="target", value=target, remove=True, ) ) return commands def _update_bond_members(self, key, want, have): commands = [] want_members = want.get(key) or [] have_members = have.get(key) or [] members_diff = list_diff_have_only(want_members, have_members) if members_diff: for member in members_diff: commands.append( self._compute_command( member["member"], "bond-group", have["name"], True, "ethernet", ) ) return commands - def _get_arp_monitor_target_diff( - self, want_list, have_list, dict_name, lst - ): + def _get_arp_monitor_target_diff(self, want_list, have_list, dict_name, lst): want_arp_target = [] have_arp_target = [] want_arp_monitor = want_list.get(dict_name) or {} if want_arp_monitor and lst in want_arp_monitor: want_arp_target = want_arp_monitor[lst] if not have_list: diff = want_arp_target else: have_arp_monitor = have_list.get(dict_name) or {} if have_arp_monitor and lst in have_arp_monitor: have_arp_target = have_arp_monitor[lst] diff = list_diff_want_only(want_arp_target, have_arp_target) return diff - def _compute_command( - self, key, attrib, value=None, remove=False, type="bonding" - ): + def _compute_command(self, key, attrib, value=None, remove=False, type="bonding"): if remove: cmd = "delete interfaces " + type else: cmd = "set interfaces " + type cmd += " " + key if attrib == "arp_monitor": attrib = "arp-monitor" elif attrib == "hash_policy": attrib = "hash-policy" cmd += " " + attrib if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py index d66e9c6..b2571e0 100644 --- a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py @@ -1,286 +1,257 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_lldp_global class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, dict_diff, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_lst_diff_for_dicts, list_diff_have_only, ) class Lldp_global(ConfigBase): """ The vyos_lldp_global class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lldp_global", ] params = ["enable", "address", "snmp", "legacy_protocols"] def __init__(self, module): super(Lldp_global, self).__init__(module) def get_lldp_global_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - lldp_global_facts = facts["ansible_network_resources"].get( - "lldp_global" - ) + lldp_global_facts = facts["ansible_network_resources"].get("lldp_global") if not lldp_global_facts: return [] return lldp_global_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_lldp_global_facts = self.get_lldp_global_facts() else: existing_lldp_global_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lldp_global_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_lldp_global_facts = self.get_lldp_global_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) result["parsed"] = self.get_lldp_global_facts(data=running_config) else: changed_lldp_global_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lldp_global_facts if result["changed"]: result["after"] = changed_lldp_global_facts elif self.state == "gathered": result["gathered"] = changed_lldp_global_facts result["warnings"] = warnings return result def set_config(self, existing_lldp_global_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_lldp_global_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "rendered") and not want: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "deleted": commands.extend(self._state_deleted(want=None, have=have)) elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(want=want, have=have)) elif self.state == "replaced": commands.extend(self._state_replaced(want=want, have=have)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._render_updates(want, have)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: for item in Lldp_global.params: if item == "legacy_protocols": commands.extend(self._update_lldp_protocols(want, have)) - elif ( - have.get(item) and not want.get(item) and item != "enable" - ): + elif have.get(item) and not want.get(item) and item != "enable": commands.append(Lldp_global.del_cmd + item) elif have: for item in Lldp_global.params: if have.get(item): if item == "legacy_protocols": - commands.append( - self._compute_command( - "legacy-protocols", remove=True - ) - ) + commands.append(self._compute_command("legacy-protocols", remove=True)) elif item == "address": - commands.append( - self._compute_command( - "management-address", remove=True - ) - ) + commands.append(self._compute_command("management-address", remove=True)) elif item == "snmp": - commands.append( - self._compute_command(item, remove=True) - ) + commands.append(self._compute_command(item, remove=True)) return commands def _render_updates(self, want, have): commands = [] if have: temp_have_legacy_protos = have.pop("legacy_protocols", None) else: have = {} temp_want_legacy_protos = want.pop("legacy_protocols", None) updates = dict_diff(have, want) if have and temp_have_legacy_protos: have["legacy_protocols"] = temp_have_legacy_protos if not have and temp_want_legacy_protos: want["legacy_protocols"] = temp_want_legacy_protos commands.extend(self._add_lldp_protocols(want, have)) if updates: for key, value in iteritems(updates): if value: if key == "enable": commands.append(self._compute_command()) elif key == "address": - commands.append( - self._compute_command( - "management-address", str(value) - ) - ) + commands.append(self._compute_command("management-address", str(value))) elif key == "snmp": if value == "disable": - commands.append( - self._compute_command(key, remove=True) - ) + commands.append(self._compute_command(key, remove=True)) else: - commands.append( - self._compute_command(key, str(value)) - ) + commands.append(self._compute_command(key, str(value))) return commands def _add_lldp_protocols(self, want, have): commands = [] diff_members = get_lst_diff_for_dicts(want, have, "legacy_protocols") for key in diff_members: commands.append(self._compute_command("legacy-protocols", key)) return commands def _update_lldp_protocols(self, want_item, have_item): commands = [] want_protocols = want_item.get("legacy_protocols") or [] have_protocols = have_item.get("legacy_protocols") or [] members_diff = list_diff_have_only(want_protocols, have_protocols) if members_diff: for member in members_diff: - commands.append( - self._compute_command( - "legacy-protocols", member, remove=True - ) - ) + commands.append(self._compute_command("legacy-protocols", member, remove=True)) return commands def _compute_command(self, key=None, value=None, remove=False): if remove: cmd = "delete service lldp" else: cmd = "set service lldp" if key: cmd += " " + key if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py index f1f299f..11b950b 100644 --- a/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py +++ b/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py @@ -1,459 +1,415 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_lldp_interfaces class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, dict_diff, + to_list, ) -from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - search_obj_in_list, - search_dict_tv_in_list, - key_value_in_dict, is_dict_element_present, + key_value_in_dict, + search_dict_tv_in_list, + search_obj_in_list, ) class Lldp_interfaces(ConfigBase): """ The vyos_lldp_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lldp_interfaces", ] params = ["enable", "location", "name"] def __init__(self, module): super(Lldp_interfaces, self).__init__(module) def get_lldp_interfaces_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - lldp_interfaces_facts = facts["ansible_network_resources"].get( - "lldp_interfaces" - ) + lldp_interfaces_facts = facts["ansible_network_resources"].get("lldp_interfaces") if not lldp_interfaces_facts: return [] return lldp_interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() else: existing_lldp_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lldp_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_lldp_interfaces_facts( - data=running_config - ) + result["parsed"] = self.get_lldp_interfaces_facts(data=running_config) else: changed_lldp_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lldp_interfaces_facts if result["changed"]: result["after"] = changed_lldp_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_lldp_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_lldp_interfaces_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_lldp_interfaces_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not want - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "overridden": commands.extend(self._state_overridden(want=want, have=have)) elif self.state == "deleted": if want: for item in want: name = item["name"] have_item = search_obj_in_list(name, have) - commands.extend( - self._state_deleted(want=None, have=have_item) - ) + commands.extend(self._state_deleted(want=None, have=have_item)) else: for have_item in have: - commands.extend( - self._state_deleted(want=None, have=have_item) - ) + commands.extend(self._state_deleted(want=None, have=have_item)) else: for want_item in want: name = want_item["name"] have_item = search_obj_in_list(name, have) if self.state in ("merged", "rendered"): - commands.extend( - self._state_merged(want=want_item, have=have_item) - ) + commands.extend(self._state_merged(want=want_item, have=have_item)) if self.state == "replaced": - commands.extend( - self._state_replaced(want=want_item, have=have_item) - ) + commands.extend(self._state_replaced(want=want_item, have=have_item)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for have_item in have: lldp_name = have_item["name"] lldp_in_want = search_obj_in_list(lldp_name, want) if not lldp_in_want: - commands.append( - self._compute_command(have_item["name"], remove=True) - ) + commands.append(self._compute_command(have_item["name"], remove=True)) for want_item in want: name = want_item["name"] lldp_in_have = search_obj_in_list(name, have) commands.extend(self._state_replaced(want_item, lldp_in_have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] if have: commands.extend(self._render_updates(want, have)) else: commands.extend(self._render_set_commands(want)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: params = Lldp_interfaces.params for attrib in params: if attrib == "location": - commands.extend( - self._update_location(have["name"], want, have) - ) + commands.extend(self._update_location(have["name"], want, have)) elif have: commands.append(self._compute_command(have["name"], remove=True)) return commands def _render_updates(self, want, have): commands = [] lldp_name = have["name"] commands.extend(self._configure_status(lldp_name, want, have)) commands.extend(self._add_location(lldp_name, want, have)) return commands def _render_set_commands(self, want): commands = [] have = {} lldp_name = want["name"] params = Lldp_interfaces.params for attrib in params: value = want[attrib] if value: if attrib == "location": commands.extend(self._add_location(lldp_name, want, have)) elif attrib == "enable": if not value: - commands.append( - self._compute_command(lldp_name, value="disable") - ) + commands.append(self._compute_command(lldp_name, value="disable")) else: commands.append(self._compute_command(lldp_name)) return commands def _configure_status(self, name, want_item, have_item): commands = [] if is_dict_element_present(have_item, "enable"): temp_have_item = False else: temp_have_item = True if want_item["enable"] != temp_have_item: if want_item["enable"]: - commands.append( - self._compute_command(name, value="disable", remove=True) - ) + commands.append(self._compute_command(name, value="disable", remove=True)) else: commands.append(self._compute_command(name, value="disable")) return commands def _add_location(self, name, want_item, have_item): commands = [] have_dict = {} have_ca = {} set_cmd = name + " location " want_location_type = want_item.get("location") or {} have_location_type = have_item.get("location") or {} if want_location_type["coordinate_based"]: want_dict = want_location_type.get("coordinate_based") or {} if is_dict_element_present(have_location_type, "coordinate_based"): have_dict = have_location_type.get("coordinate_based") or {} location_type = "coordinate-based" updates = dict_diff(have_dict, want_dict) for key, value in iteritems(updates): if value: - commands.append( - self._compute_command( - set_cmd + location_type, key, str(value) - ) - ) + commands.append(self._compute_command(set_cmd + location_type, key, str(value))) elif want_location_type["civic_based"]: location_type = "civic-based" want_dict = want_location_type.get("civic_based") or {} want_ca = want_dict.get("ca_info") or [] if is_dict_element_present(have_location_type, "civic_based"): have_dict = have_location_type.get("civic_based") or {} have_ca = have_dict.get("ca_info") or [] if want_dict["country_code"] != have_dict["country_code"]: commands.append( self._compute_command( set_cmd + location_type, "country-code", str(want_dict["country_code"]), ) ) else: commands.append( self._compute_command( set_cmd + location_type, "country-code", str(want_dict["country_code"]), ) ) commands.extend(self._add_civic_address(name, want_ca, have_ca)) elif want_location_type["elin"]: location_type = "elin" if is_dict_element_present(have_location_type, "elin"): - if want_location_type.get("elin") != have_location_type.get( - "elin" - ): + if want_location_type.get("elin") != have_location_type.get("elin"): commands.append( self._compute_command( set_cmd + location_type, value=str(want_location_type["elin"]), ) ) else: commands.append( self._compute_command( set_cmd + location_type, value=str(want_location_type["elin"]), ) ) return commands def _update_location(self, name, want_item, have_item): commands = [] del_cmd = name + " location" want_location_type = want_item.get("location") or {} have_location_type = have_item.get("location") or {} if want_location_type["coordinate_based"]: want_dict = want_location_type.get("coordinate_based") or {} if is_dict_element_present(have_location_type, "coordinate_based"): have_dict = have_location_type.get("coordinate_based") or {} location_type = "coordinate-based" for key, value in iteritems(have_dict): only_in_have = key_value_in_dict(key, value, want_dict) if not only_in_have: commands.append( - self._compute_command( - del_cmd + location_type, key, str(value), True - ) + self._compute_command(del_cmd + location_type, key, str(value), True) ) else: commands.append(self._compute_command(del_cmd, remove=True)) elif want_location_type["civic_based"]: want_dict = want_location_type.get("civic_based") or {} want_ca = want_dict.get("ca_info") or [] if is_dict_element_present(have_location_type, "civic_based"): have_dict = have_location_type.get("civic_based") or {} have_ca = have_dict.get("ca_info") - commands.extend( - self._update_civic_address(name, want_ca, have_ca) - ) + commands.extend(self._update_civic_address(name, want_ca, have_ca)) else: commands.append(self._compute_command(del_cmd, remove=True)) else: if is_dict_element_present(have_location_type, "elin"): - if want_location_type.get("elin") != have_location_type.get( - "elin" - ): - commands.append( - self._compute_command(del_cmd, remove=True) - ) + if want_location_type.get("elin") != have_location_type.get("elin"): + commands.append(self._compute_command(del_cmd, remove=True)) else: commands.append(self._compute_command(del_cmd, remove=True)) return commands def _add_civic_address(self, name, want, have): commands = [] for item in want: ca_type = item["ca_type"] ca_value = item["ca_value"] - obj_in_have = search_dict_tv_in_list( - ca_type, ca_value, have, "ca_type", "ca_value" - ) + obj_in_have = search_dict_tv_in_list(ca_type, ca_value, have, "ca_type", "ca_value") if not obj_in_have: commands.append( self._compute_command( key=name + " location civic-based ca-type", attrib=str(ca_type) + " ca-value", value=ca_value, ) ) return commands def _update_civic_address(self, name, want, have): commands = [] for item in have: ca_type = item["ca_type"] ca_value = item["ca_value"] - in_want = search_dict_tv_in_list( - ca_type, ca_value, want, "ca_type", "ca_value" - ) + in_want = search_dict_tv_in_list(ca_type, ca_value, want, "ca_type", "ca_value") if not in_want: commands.append( self._compute_command( name, "location civic-based ca-type", str(ca_type), remove=True, ) ) return commands def _compute_command(self, key, attrib=None, value=None, remove=False): if remove: cmd = "delete service lldp interface " else: cmd = "set service lldp interface " cmd += key if attrib: cmd += " " + attrib if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/config/logging_global/logging_global.py b/plugins/module_utils/network/vyos/config/logging_global/logging_global.py index 696090d..27c33b3 100644 --- a/plugins/module_utils/network/vyos/config/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/config/logging_global/logging_global.py @@ -1,237 +1,220 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_logging_global config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from copy import deepcopy from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, - get_from_dict, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, + get_from_dict, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.logging_global import ( Logging_globalTemplate, ) class Logging_global(ResourceModule): """ The vyos_logging_global config class """ def __init__(self, module): super(Logging_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="logging_global", tmplt=Logging_globalTemplate(), ) self.parsers = [ "console.facilities", "global_params.archive.file_num", "global_params.archive.size", "global_params.marker_interval", "global_params.preserve_fqdn", "global_params.facilities", "files.archive.size", "files.archive.file_num", "files", "hosts.port", "hosts", "users", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ if self.want: wantd = self.list_to_dict(self.want, "want") else: wantd = dict() if self.have: haved = self.list_to_dict(self.have, "have") else: haved = dict() if self.state in ["overridden", "replaced"]: if wantd != haved: wantx, havex = self.call_op(wantd, haved, "overridden") for k, have in iteritems(havex): if k not in wantx: self._compare(want={}, have=have) if not self.state == "deleted": wantd, haved = self.call_op(wantd, haved) if self.state == "merged": wantd = dict_merge(haved, wantd) for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Logging_global network resource. """ self.compare(parsers=self.parsers, want=want, have=have) self.handleStates(want=want, have=have) def operation_rep(self, params): op_val = dict() for k, val in iteritems(params): if k in ["console", "global_params"]: mod_val = deepcopy(val) op_val.update(self.flatten_facility({k: mod_val})) elif k in ["files", "hosts", "users"]: for m, n in iteritems(val): mod_n = deepcopy(n) if mod_n.get("archive"): del mod_n["archive"] if mod_n.get("facilities"): del mod_n["facilities"] if mod_n.get("port"): del mod_n["port"] tm = self.flatten_facility({k: {m: mod_n}}) op_val.update(tm) return op_val def call_op(self, _want, _have, mode=None): if mode == "overridden": w = self.operation_rep(_want) h = self.operation_rep(_have) else: w = self.flatten_facility(_want) h = self.flatten_facility(_have) return w, h def handleStates(self, want=None, have=None): stateparsers = [ "syslog.state", "console.state", "global_params.state", "global_params.archive.state", "files.archive.state", ] for par in stateparsers: op = get_from_dict(want, par) if op == "enabled": self.addcmd(want, par) elif op == "disabled": self.addcmd(want, par, True) break def flatten_facility(self, param): temp_param = dict() for element, val in iteritems(param): if element in ["console", "global_params", "syslog"]: if element != "syslog" and val.get("facilities"): for k, v in iteritems(val.get("facilities")): temp_param[k + element] = {element: {"facilities": v}} del val["facilities"] if val: temp_param[element] = {element: val} if element in ["files", "hosts", "users"]: for k, v in iteritems(val): if v.get("facilities"): for pk, dat in iteritems(v.get("facilities")): temp_param[pk + k] = { element: { "facilities": dat, - self.pkey.get(element): v.get( - self.pkey.get(element) - ), + self.pkey.get(element): v.get(self.pkey.get(element)), } } del v["facilities"] if len(list(v.keys())) > 1: temp_param[k] = {element: v} else: temp_param[k] = {element: v} return temp_param def list_to_dict(self, param, op=None): updated_param = dict() if self.state == "deleted": if op == "have" and param: self.handleStates({"syslog": {"state": "disabled"}}) updated_param == {} else: self.pkey = { "files": "path", "hosts": "hostname", "users": "username", } for element, val in iteritems(param): if element == "facilities": # only with recursion call _tem_par = {} for par in val: if par.get("facility") and par.get("severity"): - _tem_par.update( - { - par.get("facility") - + par.get("severity"): par - } - ) + _tem_par.update({par.get("facility") + par.get("severity"): par}) elif par.get("facility") and par.get("protocol"): - _tem_par.update( - { - par.get("facility") - + par.get("protocol"): par - } - ) + _tem_par.update({par.get("facility") + par.get("protocol"): par}) else: _tem_par.update({par.get("facility"): par}) return _tem_par elif element in ["console", "global_params", "syslog"]: if element != "syslog" and val.get("facilities"): val["facilities"] = self.list_to_dict(val) updated_param[element] = val elif element in ["hosts", "users", "files"]: for v in val: if v.get("facilities"): v["facilities"] = self.list_to_dict(v) if updated_param.get(element): - updated_param[element].update( - {v.get(self.pkey.get(element)): v} - ) + updated_param[element].update({v.get(self.pkey.get(element)): v}) else: - updated_param[element] = { - v.get(self.pkey.get(element)): v - } + updated_param[element] = {v.get(self.pkey.get(element)): v} return updated_param diff --git a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py index 0a0e389..cd677d4 100644 --- a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py @@ -1,197 +1,188 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_ntp config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ntp_global import ( NtpTemplate, ) class Ntp_global(ResourceModule): """ The vyos_ntp config class """ def __init__(self, module): super(Ntp_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="ntp_global", tmplt=NtpTemplate(), ) self.parsers = [ "allow_clients", "listen_addresses", "server", "options", "allow_clients_delete", "listen_addresses_delete", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self._ntp_list_to_dict(self.want) haved = self._ntp_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = { - k: v for k, v in iteritems(haved) if k in wantd or not wantd - } + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} wantd = {} commandlist = self._commandlist(haved) servernames = self._servernames(haved) # removing the servername and commandlist from the list after deleting it from haved for k, have in iteritems(haved): if k not in wantd: for hk, hval in iteritems(have): if hk == "allow_clients" and hk in commandlist: self.commands.append( - self._tmplt.render( - {"": hk}, "allow_clients_delete", True - ) + self._tmplt.render({"": hk}, "allow_clients_delete", True) ) commandlist.remove(hk) elif hk == "listen_addresses" and hk in commandlist: self.commands.append( - self._tmplt.render( - {"": hk}, "listen_addresses_delete", True - ) + self._tmplt.render({"": hk}, "listen_addresses_delete", True) ) commandlist.remove(hk) elif hk == "server" and have["server"] in servernames: self._compareoverride(want={}, have=have) servernames.remove(have["server"]) # remove existing config for overridden,replaced and deleted # Getting the list of the server names from haved # to avoid the duplication of overridding/replacing the servers if self.state in ["overridden", "replaced"]: commandlist = self._commandlist(haved) servernames = self._servernames(haved) for k, have in iteritems(haved): if k not in wantd and "server" not in have: self._compareoverride(want={}, have=have) # removing the servername from the list after deleting it from haved elif k not in wantd and have["server"] in servernames: self._compareoverride(want={}, have=have) servernames.remove(have["server"]) for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Ntp network resource. """ if "options" in want: self.compare(parsers="options", want=want, have=have) else: self.compare(parsers=self.parsers, want=want, have=have) def _compareoverride(self, want, have): # do not delete configuration with options level for i, val in iteritems(have): if i == "options": pass else: self.compare(parsers=i, want={}, have=have) def _ntp_list_to_dict(self, entry): servers_dict = {} for k, data in iteritems(entry): if k == "servers": for value in data: if "options" in value: result = self._serveroptions_list_to_dict(value) for res, resvalue in iteritems(result): servers_dict.update({res: resvalue}) else: servers_dict.update({value["server"]: value}) else: for value in data: servers_dict.update({"ip_" + value: {k: value}}) return servers_dict def _serveroptions_list_to_dict(self, entry): serveroptions_dict = {} for Opk, Op in iteritems(entry): if Opk == "options": for val in Op: dict = {} dict.update({"server": entry["server"]}) dict.update({Opk: val}) - serveroptions_dict.update( - {entry["server"] + "_" + val: dict} - ) + serveroptions_dict.update({entry["server"] + "_" + val: dict}) return serveroptions_dict def _commandlist(self, haved): commandlist = [] for k, have in iteritems(haved): for ck, cval in iteritems(have): if ck != "options" and ck not in commandlist: commandlist.append(ck) return commandlist def _servernames(self, haved): servernames = [] for k, have in iteritems(haved): for sk, sval in iteritems(have): if sk == "server" and sval not in [ "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", ]: if sval not in servernames: servernames.append(sval) return servernames diff --git a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py index dbdce48..527f062 100644 --- a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py @@ -1,170 +1,169 @@ # # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_ospf_interfaces config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import ( Ospf_interfacesTemplate, ) class Ospf_interfaces(ResourceModule): """ The vyos_ospf_interfaces config class """ def __init__(self, module): super(Ospf_interfaces, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="ospf_interfaces", tmplt=Ospf_interfacesTemplate(), ) self.parsers = [ "authentication_password", "authentication_md5", "bandwidth", "cost", "hello_interval", "dead_interval", "mtu_ignore", "network", "priority", "retransmit_interval", "transmit_delay", "ifmtu", "instance", "passive", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} for entry in self.want: wantd.update({entry["name"]: entry}) for entry in self.have: haved.update({entry["name"]: entry}) # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._ospf_int_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": h_del = {} for k, v in iteritems(haved): if k in wantd or not wantd: h_del.update({k: v}) haved = h_del have_int = [] for k, have in iteritems(haved): if k in wantd: have_int.append(k) self._remove_ospf_int(have) wantd = {} if self.state == "overridden": have_int = [] for k, have in iteritems(haved): if k not in wantd: have_int.append(k) self._remove_ospf_int(have) # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: # removing the interfaces from haved that are already negated for interface in have_int: haved.pop(interface) for k, have in iteritems(haved): if k not in wantd: self._compare(want={}, have=have) for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _remove_ospf_int(self, entry): int_name = entry.get("name", {}) int_addr = entry.get("address_family", {}) for k, addr in iteritems(int_addr): rem_entry = {"name": int_name, "address_family": {"afi": k}} self.addcmd(rem_entry, "ip_ospf", True) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Ospf_interfaces network resource. """ self._compare_addr_family(want=want, have=have) def _compare_addr_family(self, want, have): wdict = want.get("address_family", {}) hdict = have.get("address_family", {}) wname = want.get("name") hname = have.get("name") for name, entry in iteritems(wdict): for key, param in iteritems(entry): w_addr = {"afi": name, key: param} h_addr = {} if hdict.get(name): h_addr = {"afi": name, key: hdict[name].pop(key, {})} w = {"name": wname, "address_family": w_addr} h = {"name": hname, "address_family": h_addr} self.compare(parsers=self.parsers, want=w, have=h) for name, entry in iteritems(hdict): for key, param in iteritems(entry): h_addr = {"afi": name, key: param} w_addr = {} w = {"name": wname, "address_family": w_addr} h = {"name": hname, "address_family": h_addr} self.compare(parsers=self.parsers, want=w, have=h) def _ospf_int_list_to_dict(self, entry): for name, family in iteritems(entry): if "address_family" in family: addr_dict = {} for entry in family.get("address_family", []): addr_dict.update({entry["afi"]: entry}) family["address_family"] = addr_dict self._ospf_int_list_to_dict(family["address_family"]) diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index 059b92d..9287fbc 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -1,949 +1,810 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_ospfv2 class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, remove_empties, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - list_diff_want_only, + _bool_to_str, _in_target, _is_w_same, - _bool_to_str, + list_diff_want_only, ) class Ospfv2(ConfigBase): """ The vyos_ospfv2 class """ gather_subset = ["!all", "!min"] gather_network_resources = ["ospfv2"] def __init__(self, module): super(Ospfv2, self).__init__(module) def get_ospfv2_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ (facts, _warnings) = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) ospfv2_facts = facts["ansible_network_resources"].get("ospfv2", {}) return ospfv2_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_ospfv2_facts = self.get_ospfv2_facts() else: existing_ospfv2_facts = {} if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_ospfv2_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_ospfv2_facts = self.get_ospfv2_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) result["parsed"] = self.get_ospfv2_facts(data=running_config) else: changed_ospfv2_facts = {} if self.state in self.ACTION_STATES: result["before"] = existing_ospfv2_facts if result["changed"]: result["after"] = changed_ospfv2_facts elif self.state == "gathered": result["gathered"] = changed_ospfv2_facts result["warnings"] = warnings return result def set_config(self, existing_ospfv2_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_ospfv2_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not w - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "deleted": commands.extend(self._state_deleted(h)) elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def search_obj_in_have(self, have, w_name, key): """ This function returns the rule-set/rule if it is present in target config. :param have: target config. :param w_name: rule-set name. :param type: rule_sets/rule/r_list. :return: rule-set/rule. """ if have: for item in have: if item[key] == w_name[key]: return item return None def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._render_ospf_param(have, want, opr=False)) commands.extend(self._render_ospf_param(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._render_ospf_param(want, have)) return commands def _state_deleted(self, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if have: commands.append("delete protocols ospf") return commands def _render_ospf_param(self, want, have, opr=True): """ This function forms the set/delete commands for ospf leaf attributes and triggers the process for other child attributes. for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w = deepcopy(remove_empties(want)) leaf = ("default_metric", "log_adjacency_changes") if w: for (key, val) in iteritems(w): if opr and key in leaf and not _is_w_same(w, have, key): - commands.append( - self._form_attr_cmd( - attr=key, val=_bool_to_str(val), opr=opr - ) - ) + commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) elif not opr and key in leaf and not _in_target(have, key): - commands.append( - self._form_attr_cmd( - attr=key, val=_bool_to_str(val), opr=opr - ) - ) + commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) else: - commands.extend( - self._render_child_param(w, have, key, opr) - ) + commands.extend(self._render_child_param(w, have, key, opr)) return commands def _render_child_param(self, w, h, key, opr=True): """ This function invoke the function to extend commands based on the key. :param w: the desired configuration. :param h: the current configuration. :param key: attribute name. :param opr: operation. :return: list of commands. """ commands = [] if key in ("neighbor", "redistribute"): commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) elif key in ("default_information", "max_metric"): commands.extend(self._render_nested_dict_param(key, w, h, opr=opr)) elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"): commands.extend(self._render_dict_param(key, w, h, opr=opr)) elif key in ( "route_map", "passive_interface", "passive_interface_exclude", ): commands.extend(self._render_list_param(key, w, h, opr=opr)) elif key == "areas": commands.extend(self._render_areas(key, w, h, opr=opr)) elif key == "timers": commands.extend(self._render_timers(key, w, h, opr=opr)) elif key == "distance": commands.extend(self._render_distance(key, w, h, opr=opr)) return commands def _render_dict_param(self, attr, want, have, opr=True): """ This function generate the commands for dictionary elements. :param attr: attribute name. :param w: the desired configuration. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h = {} if have: h = have.get(attr) or {} if not opr and not h: commands.append(self._form_attr_cmd(attr=attr, opr=opr)) elif want[attr]: leaf_dict = { "auto_cost": "reference_bandwidth", "mpls_te": ("enabled", "router_address"), "parameters": ( "router_id", "abr_type", "opaque_lsa", "rfc1583_compatibility", ), } leaf = leaf_dict[attr] for (item, value) in iteritems(want[attr]): - if ( - opr - and item in leaf - and not _is_w_same(want[attr], h, item) - ): + if opr and item in leaf and not _is_w_same(want[attr], h, item): if item == "enabled": item = "enable" if item in ( "opaque_lsa", "enable", "rfc1583_compatibility", ): - commands.append( - self._form_attr_cmd(key=attr, attr=item, opr=opr) - ) + commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) else: commands.append( - self._form_attr_cmd( - key=attr, attr=item, val=value, opr=opr - ) + self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr) ) elif not opr and item in leaf and not _in_target(h, item): if item == "enabled": - commands.append( - self._form_attr_cmd( - key=attr, attr="enable", opr=opr - ) - ) + commands.append(self._form_attr_cmd(key=attr, attr="enable", opr=opr)) else: - commands.append( - self._form_attr_cmd(key=attr, attr=item, opr=opr) - ) + commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) return commands def _render_list_param(self, attr, want, have, cmd=None, opr=True): """ This function forms the commands for passed target list attributes'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated list of commands. """ commands = [] h = [] if want: w = want.get(attr) or [] if have: h = have.get(attr) or [] if not cmd: cmd = self._compute_command(opr=opr) if w: if opr: members = list_diff_want_only(w, h) for member in members: command = cmd + attr.replace("_", "-") + " " if attr == "network": command += member["address"] else: command += member commands.append(command) elif not opr: if h: for member in w: if attr == "network": - if not self.search_obj_in_have( - h, member, "address" - ): + if not self.search_obj_in_have(h, member, "address"): commands.append( - cmd - + attr.replace("_", "-") - + " " - + member["address"] + cmd + attr.replace("_", "-") + " " + member["address"] ) elif member not in h: - commands.append( - cmd + attr.replace("_", "-") + " " + member - ) + commands.append(cmd + attr.replace("_", "-") + " " + member) else: commands.append(cmd + " " + attr.replace("_", "-")) return commands def _render_vlink(self, attr, want, have, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] name = {"virtual_link": "address"} leaf_dict = { "virtual_link": ( "address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval", ) } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: commands.append(cmd + attr.replace("_", "-")) elif w: for w_item in w: for (key, val) in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) - if ( - opr - and key in leaf - and not _is_w_same(w_item, h_item, key) - ): + if opr and key in leaf and not _is_w_same(w_item, h_item, key): if key in "address": - commands.append( - cmd + attr.replace("_", "-") + " " + str(val) - ) + commands.append(cmd + attr.replace("_", "-") + " " + str(val)) else: commands.append( cmd + attr.replace("_", "-") + " " + w_item[name[attr]] + " " + key.replace("_", "-") + " " + str(val) ) - elif ( - not opr and key in leaf and not _in_target(h_item, key) - ): + elif not opr and key in leaf and not _in_target(h_item, key): if key in "address": - commands.append( - cmd + attr.replace("_", "-") + " " + str(val) - ) + commands.append(cmd + attr.replace("_", "-") + " " + str(val)) else: commands.append( - cmd - + attr.replace("_", "-") - + " " - + w_item[name[attr]] - + " " - + key + cmd + attr.replace("_", "-") + " " + w_item[name[attr]] + " " + key ) elif key == "authentication": commands.extend( self._render_vlink_auth( attr, key, w_item, h_item, w_item["address"], cmd, opr, ) ) return commands - def _render_vlink_auth( - self, attr, key, want, have, address, cmd=None, opr=True - ): + def _render_vlink_auth(self, attr, key, want, have, address, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] w = want.get(key) or {} if have: h = have.get(key) or {} cmd += attr.replace("_", "-") + " " + address + " " + key + " " commands.extend(self._render_list_dict_param("md5", w, h, cmd, opr)) return commands def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] name = { "redistribute": "route_type", "neighbor": "neighbor_id", "range": "address", "md5": "key_id", "vlink": "address", } leaf_dict = { "md5": "md5_key", "redistribute": ( "metric", "route_map", "route_type", "metric_type", ), "neighbor": ("priority", "poll_interval", "neighbor_id"), "range": ("cost", "address", "substitute", "not_advertise"), "vlink": ( "address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval", ), } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: for (key, val) in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) - if ( - opr - and key in leaf - and not _is_w_same(w_item, h_item, key) - ): + if opr and key in leaf and not _is_w_same(w_item, h_item, key): if key in ( "route_type", "neighbor_id", "address", "key_id", ): commands.append(cmd + attr + " " + str(val)) elif key == "cost": commands.append( - cmd - + attr - + " " - + w_item[name[attr]] - + " " - + key - + " " - + str(val) + cmd + attr + " " + w_item[name[attr]] + " " + key + " " + str(val) ) elif key == "not_advertise": commands.append( - cmd - + attr - + " " - + w_item[name[attr]] - + " " - + key.replace("_", "-") + cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") ) elif key == "md5_key": commands.append( cmd + attr + " " + "key-id" + " " + str(w_item[name[attr]]) + " " + key.replace("_", "-") + " " + w_item[key] ) else: commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") + " " + str(val) ) - elif ( - not opr and key in leaf and not _in_target(h_item, key) - ): + elif not opr and key in leaf and not _in_target(h_item, key): if key in ( "route_type", "neighbor_id", "address", "key_id", ): commands.append(cmd + attr + " " + str(val)) else: - commands.append( - cmd - + attr - + " " - + w_item[name[attr]] - + " " - + key - ) + commands.append(cmd + attr + " " + w_item[name[attr]] + " " + key) return commands def _render_nested_dict_param(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired nested dicts. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] attr_dict = { "default_information": "originate", "max_metric": "router_lsa", } leaf_dict = { "default_information": ( "always", "metric", "metric_type", "route_map", ), "max_metric": ("administrative", "on_startup", "on_shutdown"), } h = {} w = want.get(attr) or {} if have: h = have.get(attr) or {} if not opr and not h: commands.append(self._form_attr_cmd(attr=attr, opr=opr)) elif w: key = attr_dict[attr] w_attrib = want[attr].get(key) or {} cmd = self._compute_command(opr=opr) h_attrib = {} if w_attrib: leaf = leaf_dict[attr] if h and key in h.keys(): h_attrib = h.get(key) or {} for (item, val) in iteritems(w[key]): - if ( - opr - and item in leaf - and not _is_w_same(w[key], h_attrib, item) - ): + if opr and item in leaf and not _is_w_same(w[key], h_attrib, item): if item in ("administrative", "always") and val: commands.append( cmd + attr.replace("_", "-") + " " + key.replace("_", "-") + " " + item.replace("_", "-") ) elif item not in ("administrative", "always"): commands.append( cmd + attr.replace("_", "-") + " " + key.replace("_", "-") + " " + item.replace("_", "-") + " " + str(val) ) - elif ( - not opr - and item in leaf - and not _in_target(h_attrib, item) - ): + elif not opr and item in leaf and not _in_target(h_attrib, item): commands.append(cmd + attr + " " + item) return commands def _render_areas(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for ospf area attributes. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_lst = {} w_lst = want.get(attr) or [] l_set = ("area_id", "shortcut", "authentication") if have: h_lst = have.get(attr) or [] if not opr and not h_lst: commands.append(self._form_attr_cmd(attr="area", opr=opr)) elif w_lst: for w_area in w_lst: cmd = ( self._compute_command( key="area", attr=_bool_to_str(w_area["area_id"]), opr=opr, ) + " " ) h_area = self.search_obj_in_have(h_lst, w_area, "area_id") if not opr and not h_area: commands.append( - self._form_attr_cmd( - key="area", attr=w_area["area_id"], opr=opr - ) + self._form_attr_cmd(key="area", attr=w_area["area_id"], opr=opr) ) else: for (key, val) in iteritems(w_area): - if ( - opr - and key in l_set - and not _is_w_same(w_area, h_area, key) - ): + if opr and key in l_set and not _is_w_same(w_area, h_area, key): if key == "area_id": commands.append( self._form_attr_cmd( attr="area", val=_bool_to_str(val), opr=opr, ) ) else: commands.append( - cmd - + key - + " " - + _bool_to_str(val).replace("_", "-") + cmd + key + " " + _bool_to_str(val).replace("_", "-") ) elif not opr and key in l_set: - if key == "area_id" and not _in_target( - h_area, key - ): + if key == "area_id" and not _in_target(h_area, key): commands.append(cmd) continue - if key != "area_id" and not _in_target( - h_area, key - ): + if key != "area_id" and not _in_target(h_area, key): commands.append(cmd + val + " " + key) elif key == "area_type": - commands.extend( - self._render_area_type( - w_area, h_area, key, cmd, opr - ) - ) + commands.extend(self._render_area_type(w_area, h_area, key, cmd, opr)) elif key == "network": - commands.extend( - self._render_list_param( - key, w_area, h_area, cmd, opr - ) - ) + commands.extend(self._render_list_param(key, w_area, h_area, cmd, opr)) elif key == "range": commands.extend( - self._render_list_dict_param( - key, w_area, h_area, cmd, opr - ) + self._render_list_dict_param(key, w_area, h_area, cmd, opr) ) elif key == "virtual_link": - commands.extend( - self._render_vlink( - key, w_area, h_area, cmd, opr - ) - ) + commands.extend(self._render_vlink(key, w_area, h_area, cmd, opr)) return commands def _render_area_type(self, want, have, attr, cmd, opr=True): """ This function forms the set/delete commands based on the 'opr' type for area_types attributes. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: command to prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h_type = {} w_type = want.get(attr) or [] if have: h_type = have.get(attr) or {} if not opr and not h_type: commands.append(cmd + attr.replace("_", "-")) elif w_type: key = "normal" - if ( - opr - and key in w_type.keys() - and not _is_w_same(w_type, h_type, key) - ): + if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key): if not w_type[key] and h_type and h_type[key]: commands.append( - cmd.replace("set", "delete") - + attr.replace("_", "-") - + " " - + key + cmd.replace("set", "delete") + attr.replace("_", "-") + " " + key ) elif w_type[key]: commands.append(cmd + attr.replace("_", "-") + " " + key) - elif ( - not opr - and key in w_type.keys() - and not (h_type and key in h_type.keys()) - ): - commands.append( - cmd + want["area"] + " " + attr.replace("_", "-") - ) + elif not opr and key in w_type.keys() and not (h_type and key in h_type.keys()): + commands.append(cmd + want["area"] + " " + attr.replace("_", "-")) a_type = { "nssa": ("set", "default_cost", "no_summary", "translate"), "stub": ("set", "default_cost", "no_summary"), } for key in a_type: w_area = want[attr].get(key) or {} h_area = {} if w_area: if h_type and key in h_type.keys(): h_area = h_type.get(key) or {} for (item, val) in iteritems(w_type[key]): if ( opr and item in a_type[key] and not _is_w_same(w_type[key], h_area, item) ): if item == "set" and val: - commands.append( - cmd + attr.replace("_", "-") + " " + key - ) + commands.append(cmd + attr.replace("_", "-") + " " + key) elif not val and h_area and h_area[item]: commands.append( cmd.replace("set", "delete") + attr.replace("_", "-") + " " + key ) elif item != "set": commands.append( cmd + attr.replace("_", "-") + " " + key + " " + item.replace("_", "-") + " " + str(val) ) - elif ( - not opr - and item in a_type[key] - and not (h_type and key in h_type) - ): + elif not opr and item in a_type[key] and not (h_type and key in h_type): if item == "set": - commands.append( - cmd + attr.replace("_", "-") + " " + key - ) + commands.append(cmd + attr.replace("_", "-") + " " + key) else: commands.append( cmd + want["area"] + " " + attr.replace("_", "-") + " " + key + " " + item.replace("_", "-") ) return commands def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): """ This function forms the command for leaf attribute. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :return: generated command. """ - return self._compute_command( - key, attr=self._map_attrib(attr), val=val, opr=opr - ) + return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) - def _compute_command( - self, key=None, attr=None, val=None, remove=False, opr=True - ): + def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): """ This function construct the add/delete command based on passed attributes. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :return: generated command. """ if remove or not opr: cmd = "delete protocols ospf " else: cmd = "set protocols ospf " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val: cmd += " '" + str(val) + "'" return cmd def _map_attrib(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return "disable" if attrib == "disabled" else attrib.replace("_", "-") diff --git a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py index c43dfe7..abafc90 100644 --- a/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/config/ospfv3/ospfv3.py @@ -1,464 +1,414 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_ospfv3 class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, remove_empties, search_obj_in_list, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( + _bool_to_str, _in_target, _is_w_same, - _bool_to_str, ) class Ospfv3(ConfigBase): """ The vyos_ospfv3 class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "ospfv3", ] def __init__(self, module): super(Ospfv3, self).__init__(module) def get_ospfv3_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) ospfv3_facts = facts["ansible_network_resources"].get("ospfv3", {}) return ospfv3_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_ospfv3_facts = self.get_ospfv3_facts() else: existing_ospfv3_facts = {} if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_ospfv3_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_ospfv3_facts = self.get_ospfv3_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) result["parsed"] = self.get_ospfv3_facts(data=running_config) else: changed_ospfv3_facts = {} if self.state in self.ACTION_STATES: result["before"] = existing_ospfv3_facts if result["changed"]: result["after"] = changed_ospfv3_facts elif self.state == "gathered": result["gathered"] = changed_ospfv3_facts result["warnings"] = warnings return result def set_config(self, existing_ospfv3_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_ospfv3_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not w - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "deleted": commands.extend(self._state_deleted(w, h)) elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._render_ospf_param(have, want, opr=False)) commands.extend(self._render_ospf_param(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._render_ospf_param(want, have)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if have: commands.append("delete protocols ospfv3") return commands def _render_ospf_param(self, want, have, opr=True): """ This function forms the set/delete commands for ospf leaf attributes and triggers the process for other child attributes. for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w = deepcopy(remove_empties(want)) if w: for key, val in iteritems(w): commands.extend(self._render_child_param(w, have, key, opr)) return commands def _render_child_param(self, w, h, key, opr=True): """ This function invoke the function to extend commands based on the key. :param w: the desired configuration. :param h: the current configuration. :param key: attribute name. :param opr: operation. :return: list of commands. """ commands = [] if key == "areas": commands.extend(self._render_areas(key, w, h, opr=opr)) elif key == "parameters": commands.extend(self._render_dict_param(key, w, h, opr=opr)) elif key == "redistribute": commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) return commands def _render_dict_param(self, attr, want, have, opr=True): """ This function generate the commands for dictionary elements. :param attr: attribute name. :param w: the desired configuration. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h = {} if have: h = have.get(attr) or {} if not opr and not h: commands.append(self._form_attr_cmd(attr=attr, opr=opr)) elif want[attr]: leaf_dict = {"parameters": "router_id"} leaf = leaf_dict[attr] for item, value in iteritems(want[attr]): - if ( - opr - and item in leaf - and not _is_w_same(want[attr], h, item) - ): - commands.append( - self._form_attr_cmd( - key=attr, attr=item, val=value, opr=opr - ) - ) + if opr and item in leaf and not _is_w_same(want[attr], h, item): + commands.append(self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr)) elif not opr and item in leaf and not _in_target(h, item): - commands.append( - self._form_attr_cmd(key=attr, attr=item, opr=opr) - ) + commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) return commands def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] name = { "redistribute": "route_type", "range": "address", } leaf_dict = { "redistribute": ("route_map", "route_type"), "range": ("address", "advertise", "not_advertise"), } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: for key, val in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) - h_item = search_obj_in_list( - w_item[name[attr]], h, name[attr] - ) - if ( - opr - and key in leaf - and not _is_w_same(w_item, h_item, key) - ): + h_item = search_obj_in_list(w_item[name[attr]], h, name[attr]) + if opr and key in leaf and not _is_w_same(w_item, h_item, key): if key == "route_type" or ( key == "address" and "advertise" not in w_item and "not-advertise" not in w_item ): if not val: cmd = cmd.replace("set", "delete") commands.append(cmd + attr + " " + str(val)) elif key in leaf_dict["range"] and key != "address": commands.append( - cmd - + attr - + " " - + w_item[name[attr]] - + " " - + key.replace("_", "-") + cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") ) elif key == "route_map": commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") + " " + str(val) ) - elif ( - not opr and key in leaf and not _in_target(h_item, key) - ): + elif not opr and key in leaf and not _in_target(h_item, key): if key in ("route_type", "address"): commands.append(cmd + attr + " " + str(val)) else: - commands.append( - cmd - + (attr + " " + w_item[name[attr]] + " " + key) - ) + commands.append(cmd + (attr + " " + w_item[name[attr]] + " " + key)) return commands def _render_areas(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for ospf area attributes. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_lst = {} w_lst = want.get(attr) or [] l_set = ("area_id", "export_list", "import_list") if have: h_lst = have.get(attr) or [] if not opr and not h_lst: commands.append(self._form_attr_cmd(attr="area", opr=opr)) elif w_lst: for w_area in w_lst: cmd = ( self._compute_command( key="area", attr=_bool_to_str(w_area["area_id"]), opr=opr, ) + " " ) - h_area = search_obj_in_list( - w_area["area_id"], h_lst, "area_id" - ) + h_area = search_obj_in_list(w_area["area_id"], h_lst, "area_id") if not opr and not h_area: commands.append( - self._form_attr_cmd( - key="area", attr=w_area["area_id"], opr=opr - ) + self._form_attr_cmd(key="area", attr=w_area["area_id"], opr=opr) ) else: for key, val in iteritems(w_area): - if ( - opr - and key in l_set - and not _is_w_same(w_area, h_area, key) - ): + if opr and key in l_set and not _is_w_same(w_area, h_area, key): if key == "area_id": commands.append( self._form_attr_cmd( attr="area", val=_bool_to_str(val), opr=opr, ) ) else: commands.append( cmd + key.replace("_", "-") + " " + _bool_to_str(val).replace("_", "-") ) elif not opr and key in l_set: - if key == "area_id" and not _in_target( - h_area, key - ): + if key == "area_id" and not _in_target(h_area, key): commands.append(cmd) continue - if key != "area_id" and not _in_target( - h_area, key - ): + if key != "area_id" and not _in_target(h_area, key): commands.append(cmd + val + " " + key) elif key == "range": commands.extend( - self._render_list_dict_param( - key, w_area, h_area, cmd, opr - ) + self._render_list_dict_param(key, w_area, h_area, cmd, opr) ) return commands def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True): """ This function forms the command for leaf attribute. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :return: generated command. """ - return self._compute_command( - key, attr=self._map_attrib(attr), val=val, opr=opr - ) + return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) - def _compute_command( - self, key=None, attr=None, val=None, remove=False, opr=True - ): + def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): """ This function construct the add/delete command based on passed attributes. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :return: generated command. """ if remove or not opr: cmd = "delete protocols ospfv3 " else: cmd = "set protocols ospfv3 " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val and opr: cmd += " '" + str(val) + "'" return cmd def _map_attrib(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return "disable" if attrib == "disabled" else attrib.replace("_", "-") diff --git a/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py b/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py index b2c119d..a8cc69e 100644 --- a/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py +++ b/plugins/module_utils/network/vyos/config/prefix_lists/prefix_lists.py @@ -1,182 +1,173 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_prefix_lists config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.prefix_lists import ( Prefix_listsTemplate, ) class Prefix_lists(ResourceModule): """ The vyos_prefix_lists config class """ def __init__(self, module): super(Prefix_lists, self).__init__( empty_fact_val=[], facts_module=Facts(module), module=module, resource="prefix_lists", tmplt=Prefix_listsTemplate(), ) self.plist_parsers = [ "name", "description", ] self.entries_parsers = [ "sequence", "action", "rule_description", "ge", "le", "prefix", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {entry["afi"]: entry for entry in self.want} haved = {entry["afi"]: entry for entry in self.have} self._prefix_list_list_to_dict(wantd) self._prefix_list_list_to_dict(haved) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = { - k: v for k, v in iteritems(haved) if k in wantd or not wantd - } + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} for key, hvalue in iteritems(haved): wvalue = wantd.pop(key, {}) if wvalue: wplists = wvalue.get("prefix_lists", {}) hplists = hvalue.get("prefix_lists", {}) hvalue["prefix_lists"] = { - k: v - for k, v in iteritems(hplists) - if k in wplists or not wplists + k: v for k, v in iteritems(hplists) if k in wplists or not wplists } # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: for k, have in iteritems(haved): if k not in wantd: self._compare(want={}, have=have) for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Prefix_lists network resource. """ wplists = want.get("prefix_lists", {}) hplists = have.get("prefix_lists", {}) self._compare_plists(want=wplists, have=hplists) if self.state in ["overridden", "deleted"]: # remove remaining prefix lists for h in hplists.values(): self.commands.append( "delete policy prefix-{0} {1}".format( "list" if h["afi"] == "ipv4" else "list6", h["name"] ) ) def _compare_plists(self, want, have): for wk, wentry in iteritems(want): hentry = have.pop(wk, {}) # parser list for name and descriptions self.compare( parsers=self.plist_parsers, want=wentry, have=hentry, ) wplrules = wentry.get("entries", {}) hplrules = hentry.get("entries", {}) self._compare_rules(want=wplrules, have=hplrules) def _compare_rules(self, want, have): for wr, wrule in iteritems(want): hrule = have.pop(wr, {}) # parser list for entries self.compare( parsers=self.entries_parsers, want=wrule, have=hrule, ) # remove remaining entries for hr in have.values(): self.commands.append( "delete policy prefix-{0} {1} rule {2}".format( "list" if hr["afi"] == "ipv4" else "list6", hr["name"], hr["sequence"], ) ) def _prefix_list_list_to_dict(self, entry): for afi, value in iteritems(entry): if "prefix_lists" in value: for pl in value["prefix_lists"]: pl.update({"afi": afi}) if "entries" in pl: for entry in pl["entries"]: entry.update({"afi": afi, "name": pl["name"]}) - pl["entries"] = { - x["sequence"]: x for x in pl["entries"] - } - value["prefix_lists"] = { - entry["name"]: entry for entry in value["prefix_lists"] - } + pl["entries"] = {x["sequence"]: x for x in pl["entries"]} + value["prefix_lists"] = {entry["name"]: entry for entry in value["prefix_lists"]} diff --git a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py index c719e6d..dd6fbf6 100644 --- a/plugins/module_utils/network/vyos/config/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/config/route_maps/route_maps.py @@ -1,160 +1,154 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_route_maps config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( Route_mapsTemplate, ) class Route_maps(ResourceModule): """ The vyos_route_maps config class """ def __init__(self, module): super(Route_maps, self).__init__( empty_fact_val=[], facts_module=Facts(module), module=module, resource="route_maps", tmplt=Route_mapsTemplate(), ) self.parsers = [ "call", "description", "action", "continue_sequence", "set_aggregator_ip", "set_aggregator_as", "set_as_path_exclude", "set_as_path_prepend", "set_atomic_aggregate", "set_bgp_extcommunity_rt", "set_extcommunity_rt", "set_extcommunity_soo", "set_ip_next_hop", "set_ipv6_next_hop", "set_large_community", "set_local_preference", "set_metric", "set_metric_type", "set_origin", "set_originator_id", "set_src", "set_tag", "set_weight", "set_comm_list", "set_comm_list_delete", "set_community", "match_as_path", "match_community_community_list", "match_community_exact_match", "match_extcommunity", "match_interface", "match_large_community_large_community_list", "match_metric", "match_origin", "match_peer", "match_ip_address", "match_ip_next_hop", "match_ip_route_source", "on_match_goto", "on_match_next", "match_ipv6_address", "match_ipv6_nexthop", "match_rpki", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self._route_maps_list_to_dict(self.want) haved = self._route_maps_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": - haved = { - k: v for k, v in iteritems(haved) if k in wantd or not wantd - } + haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} wantd = {} # remove superfluous config for overridden and deleted if self.state in ["overridden", "deleted"]: for k, have in iteritems(haved): if k not in wantd: - self.commands.append( - self._tmplt.render({"route_map": k}, "route_map", True) - ) + self.commands.append(self._tmplt.render({"route_map": k}, "route_map", True)) for wk, want in iteritems(wantd): self._compare(want=want, have=haved.pop(wk, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Route_maps network resource. """ w_entries = want.get("entries", {}) h_entries = have.get("entries", {}) self._compare_entries(want=w_entries, have=h_entries) def _compare_entries(self, want, have): for wk, wentry in iteritems(want): hentry = have.pop(wk, {}) self.compare(parsers=self.parsers, want=wentry, have=hentry) def _route_maps_list_to_dict(self, entry): entry = {x["route_map"]: x for x in entry} for rmap, data in iteritems(entry): if "entries" in data: for x in data["entries"]: x.update({"route_map": rmap}) data["entries"] = { - (rmap, entry.get("sequence")): entry - for entry in data["entries"] + (rmap, entry.get("sequence")): entry for entry in data["entries"] } return entry diff --git a/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py index 5ddd429..b942f8b 100644 --- a/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/config/snmp_server/snmp_server.py @@ -1,317 +1,305 @@ # # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_snmp_server config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ +import re + from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - dict_merge, -) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( + dict_merge, + get_from_dict, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.snmp_server import ( Snmp_serverTemplate, ) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - get_from_dict, -) -import re class Snmp_server(ResourceModule): """ The vyos_snmp_server config class """ def __init__(self, module): super(Snmp_server, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="snmp_server", tmplt=Snmp_serverTemplate(), ) self.parsers = [ "contact", "description", "location", "smux_peer", "trap_source", "trap_target", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self._snmp_server_list_to_dict(self.want) haved = self._snmp_server_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": if haved: self.commands.append("delete service snmp") if self.state != "deleted": self._compare(want=wantd, have=haved) if self.state not in ["merged", "deleted"]: self._move_negate_commands() def _move_negate_commands(self): command_set = [] for cmd in self.commands: if re.search("delete service snmp", cmd): command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Snmp_server network resource. """ self._compare_lists(want, have) self._compare_snmp_v3(want, have) for key, value in iteritems(want): self.compare( parsers=self.parsers, want={key: value}, have={key: have.pop(key, "")}, ) for key, entry in iteritems(have): if entry: self.compare(parsers=self.parsers, want={}, have={key: entry}) def _compare_lists(self, want, have): parsers = [ "communities", "listen_addresses", ] for attrib in parsers: wdict = get_from_dict(want, attrib) or {} hdict = get_from_dict(have, attrib) or {} for key, entry in iteritems(wdict): # self.addcmd(entry, attrib, False) if attrib == "communities": for k, v in iteritems(entry): if k in ["clients", "networks"]: v.sort() h = {} if k != "name": if hdict.get(key): h = { "communities": { k: hdict[key].pop(k, ""), "name": key, } } self.compare( parsers="communities", want={"communities": {k: v, "name": key}}, have=h, ) else: self.compare( parsers="listen_addresses", want={"listen_addresses": entry}, have={"listen_addresses": hdict.pop(key, {})}, ) have.pop(attrib, {}) # remove remaining items in have for replaced for key, entry in iteritems(hdict): if attrib == "communities": for k, v in iteritems(entry): if k != "name": self.compare( parsers="communities", have={"communities": {k: v, "name": key}}, want={}, ) else: - self.compare( - parsers=parsers, want={}, have={attrib: entry} - ) + self.compare(parsers=parsers, want={}, have={attrib: entry}) def _compare_snmp_v3(self, want, have): parsers = [ "snmp_v3.groups", "snmp_v3.trap_targets.port", "snmp_v3.trap_targets.protocol", "snmp_v3.trap_targets.type", "snmp_v3.trap_targets.user", "snmp_v3.users.group", "snmp_v3.users.mode", "snmp_v3.views", "snmp_v3.engine_id", ] attribute_dict = { "groups": "group", "trap_targets": "address", "users": "user", "views": "view", } wdict = get_from_dict(want, "snmp_v3") or {} hdict = get_from_dict(have, "snmp_v3") or {} for attrib in attribute_dict.keys(): wattrib = get_from_dict(wdict, attrib) or {} hattrib = get_from_dict(hdict, attrib) or {} for key, entry in iteritems(wattrib): - self._compare_snmp_v3_auth_privacy( - entry, hattrib.get(key, {}), attrib - ) + self._compare_snmp_v3_auth_privacy(entry, hattrib.get(key, {}), attrib) for k, v in iteritems(entry): if k != attribute_dict[attrib]: h = {} if hattrib.get(key): h = { "snmp_v3": { attrib: { k: hattrib[key].pop(k, ""), attribute_dict[attrib]: hattrib[key][ attribute_dict[attrib] ], } } } self.compare( parsers=parsers, want={ "snmp_v3": { attrib: { k: v, - attribute_dict[attrib]: entry[ - attribute_dict[attrib] - ], + attribute_dict[attrib]: entry[attribute_dict[attrib]], } } }, have=h, ) # remove remaining items in have for replaced for key, entry in iteritems(hattrib): self._compare_snmp_v3_auth_privacy({}, entry, attrib) - self.compare( - parsers=parsers, want={}, have={"snmp_v3": {attrib: entry}} - ) + self.compare(parsers=parsers, want={}, have={"snmp_v3": {attrib: entry}}) hdict.pop(attrib, {}) for key, entry in iteritems(wdict): # self.addcmd(entry, attrib, False) self.compare( parsers="snmp_v3.engine_id", want={"snmp_v3": {key: entry}}, have={"snmp_v3": {key: hdict.pop(key, {})}}, ) # remove remaining items in have for replaced for key, entry in iteritems(hdict): - self.compare( - parsers=parsers, want={}, have={"snmp_v3": {key: entry}} - ) + self.compare(parsers=parsers, want={}, have={"snmp_v3": {key: entry}}) def _compare_snmp_v3_auth_privacy(self, wattrib, hattrib, attrib): parsers = [ "snmp_v3.trap_targets.authentication", "snmp_v3.trap_targets.privacy", "snmp_v3.users.authentication", "snmp_v3.users.privacy", ] if attrib in ["trap_targets", "users"]: if attrib == "users": primary_key = "user" else: primary_key = "address" for key, entry in iteritems(wattrib): if key != primary_key and key in ["authentication", "privacy"]: self.compare( parsers=parsers, want={ "snmp_v3": { attrib: { key: entry, primary_key: wattrib[primary_key], } } }, have={ "snmp_v3": { attrib: { key: hattrib.pop(key, {}), primary_key: wattrib[primary_key], } } }, ) for key, entry in iteritems(hattrib): if key != primary_key and key in ["authentication", "privacy"]: self.compare( parsers=parsers, want={}, have={ "snmp_v3": { attrib: { key: entry, primary_key: hattrib[primary_key], } } }, ) def _snmp_server_list_to_dict(self, entry): param_dict = { "communities": "name", "listen_addresses": "address", } v3_param_dict = { "groups": "group", "users": "user", "views": "view", "trap_targets": "address", } for k, v in iteritems(param_dict): if k in entry: a_dict = {} for el in entry[k]: a_dict.update({el[v]: el}) entry[k] = a_dict for k, v in iteritems(v3_param_dict): if entry.get("snmp_v3") and k in entry.get("snmp_v3"): a_dict = {} for el in entry["snmp_v3"][k]: a_dict.update({el[v]: el}) entry["snmp_v3"][k] = a_dict return entry diff --git a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py index bd7fd15..230a47c 100644 --- a/plugins/module_utils/network/vyos/config/static_routes/static_routes.py +++ b/plugins/module_utils/network/vyos/config/static_routes/static_routes.py @@ -1,616 +1,574 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_static_routes class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy + +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, dict_diff, remove_empties, + to_list, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible.module_utils.six import iteritems + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - get_route_type, + dict_delete, get_lst_diff_for_dicts, get_lst_same_for_dicts, - dict_delete, + get_route_type, ) class Static_routes(ConfigBase): """ The vyos_static_routes class """ gather_subset = ["!all", "!min"] gather_network_resources = ["static_routes"] def __init__(self, module): super(Static_routes, self).__init__(module) def get_static_routes_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data ) - static_routes_facts = facts["ansible_network_resources"].get( - "static_routes" - ) + static_routes_facts = facts["ansible_network_resources"].get("static_routes") if not static_routes_facts: return [] return static_routes_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_static_routes_facts = self.get_static_routes_facts() else: existing_static_routes_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_static_routes_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_static_routes_facts = self.get_static_routes_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed" ) - result["parsed"] = self.get_static_routes_facts( - data=running_config - ) + result["parsed"] = self.get_static_routes_facts(data=running_config) else: changed_static_routes_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_static_routes_facts if result["changed"]: result["after"] = changed_static_routes_facts elif self.state == "gathered": result["gathered"] = changed_static_routes_facts result["warnings"] = warnings return result def set_config(self, existing_static_routes_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_static_routes_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] - if ( - self.state in ("merged", "replaced", "overridden", "rendered") - and not want - ): + if self.state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - self.state - ) + msg="value of config parameter must not be empty for state {0}".format(self.state) ) if self.state == "overridden": commands.extend(self._state_overridden(want=want, have=have)) elif self.state == "deleted": commands.extend(self._state_deleted(want=want, have=have)) elif want: routes = self._get_routes(want) for r in routes: h_item = self.search_route_in_have(have, r["dest"]) if self.state in ("merged", "rendered"): commands.extend(self._state_merged(want=r, have=h_item)) elif self.state == "replaced": commands.extend(self._state_replaced(want=r, have=h_item)) return commands def search_route_in_have(self, have, want_dest): """ This function returns the route if its found in have config. :param have: :param dest: :return: the matched route """ routes = self._get_routes(have) for r in routes: if r["dest"] == want_dest: return r return None def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for key, value in iteritems(want): if value: if key == "next_hops": commands.extend(self._update_next_hop(want, have)) elif key == "blackhole_config": - commands.extend( - self._update_blackhole(key, want, have) - ) + commands.extend(self._update_blackhole(key, want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] routes = self._get_routes(have) for r in routes: route_in_want = self.search_route_in_have(want, r["dest"]) if not route_in_want: commands.append(self._compute_command(r["dest"], remove=True)) routes = self._get_routes(want) for r in routes: route_in_have = self.search_route_in_have(have, r["dest"]) commands.extend(self._state_replaced(r, route_in_have)) return commands def _state_merged(self, want, have, opr=True): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] if have: commands.extend(self._render_updates(want, have)) else: commands.extend(self._render_set_commands(want)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: routes = self._get_routes(want) if not routes: for w in want: af = w["address_families"] for item in af: if self.afi_in_have(have, item): - commands.append( - self._compute_command( - afi=item["afi"], remove=True - ) - ) + commands.append(self._compute_command(afi=item["afi"], remove=True)) else: routes = self._get_routes(have) if self._is_ip_route_exist(routes): commands.append(self._compute_command(afi="ipv4", remove=True)) if self._is_ip_route_exist(routes, "route6"): commands.append(self._compute_command(afi="ipv6", remove=True)) return commands def _render_set_commands(self, want): """ This function returns the list of commands to add attributes which are present in want :param want: :return: list of commands. """ commands = [] have = {} for key, value in iteritems(want): if value: if key == "dest": commands.append(self._compute_command(dest=want["dest"])) elif key == "blackhole_config": commands.extend(self._add_blackhole(key, want, have)) elif key == "next_hops": commands.extend(self._add_next_hop(want, have)) return commands def _add_blackhole(self, key, want, have): """ This function gets the diff for blackhole config specific attributes and form the commands for attributes which are present in want but not in have. :param key: :param want: :param have: :return: list of commands """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_blackhole = want_copy.get(key) or {} have_blackhole = have_copy.get(key) or {} updates = dict_delete(want_blackhole, have_blackhole) if updates: for attrib, value in iteritems(updates): if value: if attrib == "distance": commands.append( self._compute_command( dest=want["dest"], key="blackhole", attrib=attrib, remove=False, value=str(value), ) ) elif attrib == "type": - commands.append( - self._compute_command( - dest=want["dest"], key="blackhole" - ) - ) + commands.append(self._compute_command(dest=want["dest"], key="blackhole")) return commands def _add_next_hop(self, want, have, opr=True): """ This function gets the diff for next hop specific attributes and form the commands to add attributes which are present in want but not in have. :param want: :param have: :return: list of commands. """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) if not opr: - diff_next_hops = get_lst_same_for_dicts( - want_copy, have_copy, "next_hops" - ) + diff_next_hops = get_lst_same_for_dicts(want_copy, have_copy, "next_hops") else: - diff_next_hops = get_lst_diff_for_dicts( - want_copy, have_copy, "next_hops" - ) + diff_next_hops = get_lst_diff_for_dicts(want_copy, have_copy, "next_hops") if diff_next_hops: for hop in diff_next_hops: for element in hop: if element == "forward_router_address": commands.append( self._compute_command( dest=want["dest"], key="next-hop", value=hop[element], opr=opr, ) ) elif element == "enabled" and not hop[element]: commands.append( self._compute_command( dest=want["dest"], key="next-hop", attrib=hop["forward_router_address"], value="disable", opr=opr, ) ) elif element == "admin_distance": commands.append( self._compute_command( dest=want["dest"], key="next-hop", - attrib=hop["forward_router_address"] - + " " - + "distance", + attrib=hop["forward_router_address"] + " " + "distance", value=str(hop[element]), opr=opr, ) ) elif element == "interface": commands.append( self._compute_command( dest=want["dest"], key="next-hop", - attrib=hop["forward_router_address"] - + " " - + "next-hop-interface", + attrib=hop["forward_router_address"] + " " + "next-hop-interface", value=hop[element], opr=opr, ) ) return commands def _update_blackhole(self, key, want, have): """ This function gets the difference for blackhole dict and form the commands to delete the attributes which are present in have but not in want. :param want: :param have: :return: list of commands :param key: :param want: :param have: :return: list of commands """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_blackhole = want_copy.get(key) or {} have_blackhole = have_copy.get(key) or {} updates = dict_delete(have_blackhole, want_blackhole) if updates: for attrib, value in iteritems(updates): if value: if attrib == "distance": commands.append( self._compute_command( dest=want["dest"], key="blackhole", attrib=attrib, remove=True, value=str(value), ) ) - elif ( - attrib == "type" - and "distance" not in want_blackhole.keys() - ): + elif attrib == "type" and "distance" not in want_blackhole.keys(): commands.append( - self._compute_command( - dest=want["dest"], key="blackhole", remove=True - ) + self._compute_command(dest=want["dest"], key="blackhole", remove=True) ) return commands def _update_next_hop(self, want, have, opr=True): """ This function gets the difference for next_hops list and form the commands to delete the attributes which are present in have but not in want. :param want: :param have: :return: list of commands """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) - diff_next_hops = get_lst_diff_for_dicts( - have_copy, want_copy, "next_hops" - ) + diff_next_hops = get_lst_diff_for_dicts(have_copy, want_copy, "next_hops") if diff_next_hops: for hop in diff_next_hops: for element in hop: if element == "forward_router_address": commands.append( self._compute_command( dest=want["dest"], key="next-hop", value=hop[element], remove=True, ) ) elif element == "enabled": commands.append( self._compute_command( dest=want["dest"], key="next-hop", attrib=hop["forward_router_address"], value="disable", remove=True, ) ) elif element == "admin_distance": commands.append( self._compute_command( dest=want["dest"], key="next-hop", - attrib=hop["forward_router_address"] - + " " - + "distance", + attrib=hop["forward_router_address"] + " " + "distance", value=str(hop[element]), remove=True, ) ) elif element == "interface": commands.append( self._compute_command( dest=want["dest"], key="next-hop", - attrib=hop["forward_router_address"] - + " " - + "next-hop-interface", + attrib=hop["forward_router_address"] + " " + "next-hop-interface", value=hop[element], remove=True, ) ) return commands def _render_updates(self, want, have, opr=True): """ This function takes the diff between want and have and invokes the appropriate functions to create the commands to update the attributes. :param want: :param have: :return: list of commands """ commands = [] want_nh = want.get("next_hops") or [] # delete static route operation per destination if not opr and not want_nh: - commands.append( - self._compute_command(dest=want["dest"], remove=True) - ) + commands.append(self._compute_command(dest=want["dest"], remove=True)) else: temp_have_next_hops = have.pop("next_hops", None) temp_want_next_hops = want.pop("next_hops", None) updates = dict_diff(have, want) if temp_have_next_hops: have["next_hops"] = temp_have_next_hops if temp_want_next_hops: want["next_hops"] = temp_want_next_hops commands.extend(self._add_next_hop(want, have, opr=opr)) if opr and updates: for key, value in iteritems(updates): if value: if key == "blackhole_config": - commands.extend( - self._add_blackhole(key, want, have) - ) + commands.extend(self._add_blackhole(key, want, have)) return commands def _compute_command( self, dest=None, key=None, attrib=None, value=None, remove=False, afi=None, opr=True, ): """ This functions construct the required command based on the passed arguments. :param dest: :param key: :param attrib: :param value: :param remove: :return: constructed command """ if remove or not opr: cmd = "delete protocols static " + self.get_route_type(dest, afi) else: cmd = "set protocols static " + self.get_route_type(dest, afi) if dest: cmd += " " + dest if key: cmd += " " + key if attrib: cmd += " " + attrib if value: cmd += " '" + value + "'" return cmd def afi_in_have(self, have, w_item): """ This functions checks for the afi list in have :param have: :param w_item: :return: """ if have: for h in have: af = h.get("address_families") or [] for item in af: if w_item["afi"] == item["afi"]: return True return False def get_route_type(self, dest=None, afi=None): """ This function returns the route type based on destination ip address or afi :param address: :return: """ if dest: return get_route_type(dest) elif afi == "ipv4": return "route" elif afi == "ipv6": return "route6" def _is_ip_route_exist(self, routes, type="route"): """ This functions checks for the type of route. :param routes: :param type: :return: True/False """ for r in routes: if type == self.get_route_type(r["dest"]): return True return False def _get_routes(self, lst): """ This function returns the list of routes :param lst: list of address families :return: list of routes """ r_list = [] for item in lst: af = item["address_families"] for element in af: routes = element.get("routes") or [] for r in routes: r_list.append(r) return r_list diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py index a7296f2..5d60184 100644 --- a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py @@ -1,101 +1,87 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos bgp_address_family fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( + Bgp_address_familyArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import ( Bgp_address_familyTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( - Bgp_address_familyArgs, -) class Bgp_address_familyFacts(object): """The vyos bgp_address_family facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Bgp_address_familyArgs.argument_spec def get_device_data(self, connection): - return connection.get( - 'show configuration commands | match "set protocols bgp"' - ) + return connection.get('show configuration commands | match "set protocols bgp"') def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Bgp_address_family network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_device_data(connection) for resource in data.splitlines(): if "address-family" in resource: config_lines.append(re.sub("'", "", resource)) # parse native config using the Bgp_address_family template - bgp_address_family_parser = Bgp_address_familyTemplate( - lines=config_lines - ) + bgp_address_family_parser = Bgp_address_familyTemplate(lines=config_lines) objs = bgp_address_family_parser.parse() if objs: if "address_family" in objs: objs["address_family"] = list(objs["address_family"].values()) for af in objs["address_family"]: if "networks" in af: - af["networks"] = sorted( - af["networks"], key=lambda k: k["prefix"] - ) + af["networks"] = sorted(af["networks"], key=lambda k: k["prefix"]) if "aggregate_address" in af: af["aggregate_address"] = sorted( af["aggregate_address"], key=lambda k: k["prefix"] ) if "neighbors" in objs: objs["neighbors"] = list(objs["neighbors"].values()) - objs["neighbors"] = sorted( - objs["neighbors"], key=lambda k: k["neighbor_address"] - ) + objs["neighbors"] = sorted(objs["neighbors"], key=lambda k: k["neighbor_address"]) for neigh in objs["neighbors"]: if "address_family" in neigh: - neigh["address_family"] = list( - neigh["address_family"].values() - ) + neigh["address_family"] = list(neigh["address_family"].values()) - ansible_facts["ansible_network_resources"].pop( - "bgp_address_family", None - ) + ansible_facts["ansible_network_resources"].pop("bgp_address_family", None) - params = utils.remove_empties( - utils.validate_config(self.argument_spec, {"config": objs}) - ) + params = utils.remove_empties(utils.validate_config(self.argument_spec, {"config": objs})) facts["bgp_address_family"] = params.get("config", []) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py index 1efd877..c6105a0 100644 --- a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py @@ -1,92 +1,80 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos bgp_global fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import ( + Bgp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import ( Bgp_globalTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import ( - Bgp_globalArgs, -) -import re class Bgp_globalFacts(object): """The vyos bgp_global facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Bgp_globalArgs.argument_spec def get_device_data(self, connection): - return connection.get( - 'show configuration commands | match "set protocols bgp"' - ) + return connection.get('show configuration commands | match "set protocols bgp"') def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Bgp_global network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = {} config_lines = [] if not data: data = self.get_device_data(connection) for resource in data.splitlines(): if "address-family" not in resource: config_lines.append(re.sub("'", "", resource)) - bgp_global_parser = Bgp_globalTemplate( - lines=config_lines, module=self._module - ) + bgp_global_parser = Bgp_globalTemplate(lines=config_lines, module=self._module) objs = bgp_global_parser.parse() if "neighbor" in objs: objs["neighbor"] = list(objs["neighbor"].values()) - objs["neighbor"] = sorted( - objs["neighbor"], key=lambda k: k["address"] - ) + objs["neighbor"] = sorted(objs["neighbor"], key=lambda k: k["address"]) if "network" in objs: - objs["network"] = sorted( - objs["network"], key=lambda k: k["address"] - ) + objs["network"] = sorted(objs["network"], key=lambda k: k["address"]) if "aggregate_address" in objs: - objs["aggregate_address"] = sorted( - objs["aggregate_address"], key=lambda k: k["prefix"] - ) + objs["aggregate_address"] = sorted(objs["aggregate_address"], key=lambda k: k["prefix"]) ansible_facts["ansible_network_resources"].pop("bgp_global", None) params = utils.remove_empties( - bgp_global_parser.validate_config( - self.argument_spec, {"config": objs}, redact=True - ) + bgp_global_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) ) facts["bgp_global"] = params.get("config", []) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 867c427..f7b2b4e 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -1,135 +1,129 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The facts class for vyos this file validates each subset of facts and selectively calls the appropriate facts gathering function """ from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( FactsBase, ) + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family import ( + Bgp_address_familyFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import ( + Bgp_globalFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_global.firewall_global import ( + Firewall_globalFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import ( + Firewall_interfacesFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( + Firewall_rulesFacts, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.hostname.hostname import ( + HostnameFacts, +) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import ( InterfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import ( L3_interfacesFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( Lag_interfacesFacts, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( + Config, + Default, + Neighbors, +) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import ( Lldp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesFacts, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( - Firewall_rulesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( - Static_routesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_global.firewall_global import ( - Firewall_globalFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import ( - Firewall_interfacesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import ( - Ospfv3Facts, +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.logging_global.logging_global import ( + Logging_globalFacts, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import ( - Ospfv2Facts, +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ntp_global.ntp_global import ( + Ntp_globalFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospf_interfaces.ospf_interfaces import ( Ospf_interfacesFacts, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import ( - Bgp_globalFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family import ( - Bgp_address_familyFacts, +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2 import ( + Ospfv2Facts, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps import ( - Route_mapsFacts, +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3 import ( + Ospfv3Facts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.prefix_lists.prefix_lists import ( Prefix_listsFacts, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.logging_global.logging_global import ( - Logging_globalFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ntp_global.ntp_global import ( - Ntp_globalFacts, +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps import ( + Route_mapsFacts, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.snmp_server.snmp_server import ( Snmp_serverFacts, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.hostname.hostname import ( - HostnameFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( - Default, - Neighbors, - Config, +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( + Static_routesFacts, ) - FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config) FACT_RESOURCE_SUBSETS = dict( interfaces=InterfacesFacts, l3_interfaces=L3_interfacesFacts, lag_interfaces=Lag_interfacesFacts, lldp_global=Lldp_globalFacts, lldp_interfaces=Lldp_interfacesFacts, static_routes=Static_routesFacts, firewall_rules=Firewall_rulesFacts, firewall_global=Firewall_globalFacts, firewall_interfaces=Firewall_interfacesFacts, ospfv3=Ospfv3Facts, ospfv2=Ospfv2Facts, ospf_interfaces=Ospf_interfacesFacts, bgp_global=Bgp_globalFacts, bgp_address_family=Bgp_address_familyFacts, route_maps=Route_mapsFacts, prefix_lists=Prefix_listsFacts, logging_global=Logging_globalFacts, ntp_global=Ntp_globalFacts, snmp_server=Snmp_serverFacts, hostname=HostnameFacts, ) class Facts(FactsBase): """The fact class for vyos""" VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) def __init__(self, module): super(Facts, self).__init__(module) - def get_facts( - self, legacy_facts_type=None, resource_facts_type=None, data=None - ): + def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None): """Collect the facts for vyos :param legacy_facts_type: List of legacy facts types :param resource_facts_type: List of resource fact types :param data: previously collected conf :rtype: dict :return: the facts gathered """ if self.VALID_RESOURCE_SUBSETS: - self.get_network_resources_facts( - FACT_RESOURCE_SUBSETS, resource_facts_type, data - ) + self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data) if self.VALID_LEGACY_GATHER_SUBSETS: - self.get_network_legacy_facts( - FACT_LEGACY_SUBSETS, legacy_facts_type - ) + self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type) return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py index 1b1076c..6441c26 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py @@ -1,403 +1,401 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos firewall_global fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy -from re import findall, search, M -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import ( Firewall_globalArgs, ) class Firewall_globalFacts(object): """The vyos firewall_global fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_globalArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for firewall_global :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) objs = {} firewalls = findall(r"^set firewall .*$", data, M) if firewalls: objs = self.render_config(firewalls) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["firewall_global"] = utils.remove_empties(params["config"]) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join( filter( lambda x: ("firewall ipv6-name" and "firewall name" not in x), conf, ) ) a_lst = [ "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ] firewall = self.parse_attr(conf, a_lst) f_sub = { "ping": self.parse_ping(conf), "group": self.parse_group(conf), "route_redirects": self.route_redirects(conf), "state_policy": self.parse_state_policy(conf), } firewall.update(f_sub) return firewall def route_redirects(self, conf): """ This function forms the regex to fetch the afi and invoke functions to fetch route redirects and source routes :param conf: configuration data. :return: generated rule list configuration. """ rr_lst = [] v6_attr = findall( r"^set firewall (?:ipv6-src-route|ipv6-receive-redirects) (\S+)", conf, M, ) if v6_attr: obj = self.parse_rr_attrib(conf, "ipv6") if obj: rr_lst.append(obj) v4_attr = findall( r"^set firewall (?:ip-src-route|receive-redirects|send-redirects) (\S+)", conf, M, ) if v4_attr: obj = self.parse_rr_attrib(conf, "ipv4") if obj: rr_lst.append(obj) return rr_lst def parse_rr_attrib(self, conf, attrib=None): """ This function fetches the 'ip_src_route' invoke function to parse icmp redirects. :param conf: configuration to be parsed. :param attrib: 'ipv4/ipv6'. :return: generated config dictionary. """ cfg_dict = self.parse_attr(conf, ["ip_src_route"], type=attrib) cfg_dict["icmp_redirects"] = self.parse_icmp_redirects(conf, attrib) cfg_dict["afi"] = attrib return cfg_dict def parse_icmp_redirects(self, conf, attrib=None): """ This function triggers the parsing of 'icmp_redirects' attributes. :param conf: configuration to be parsed. :param attrib: 'ipv4/ipv6'. :return: generated config dictionary. """ a_lst = ["send", "receive"] cfg_dict = self.parse_attr(conf, a_lst, type=attrib) return cfg_dict def parse_ping(self, conf): """ This function triggers the parsing of 'ping' attributes. :param conf: configuration to be parsed. :return: generated config dictionary. """ a_lst = ["all", "broadcast"] cfg_dict = self.parse_attr(conf, a_lst) return cfg_dict def parse_state_policy(self, conf): """ This function fetched the connecton type and invoke function to parse other state-policy attributes. :param conf: configuration data. :return: generated rule list configuration. """ sp_lst = [] attrib = "state-policy" policies = findall(r"^set firewall " + attrib + " (\\S+)", conf, M) if policies: rules_lst = [] for sp in set(policies): sp_regex = r" %s .+$" % sp cfg = "\n".join(findall(sp_regex, conf, M)) obj = self.parse_policies(cfg, sp) obj["connection_type"] = sp if obj: rules_lst.append(obj) sp_lst = sorted(rules_lst, key=lambda i: i["connection_type"]) return sp_lst def parse_policies(self, conf, attrib=None): """ This function triggers the parsing of policy attributes action and log. :param conf: configuration :param attrib: connection type. :return: generated rule configuration dictionary. """ a_lst = ["action", "log"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_group(self, conf): """ This function triggers the parsing of 'group' attributes. :param conf: configuration. :return: generated config dictionary. """ cfg_dict = {} - cfg_dict["port_group"] = self.parse_group_lst( - conf, "port-group", False - ) + cfg_dict["port_group"] = self.parse_group_lst(conf, "port-group", False) cfg_dict["address_group"] = self.parse_group_lst( conf, "address-group" ) + self.parse_group_lst(conf, "ipv6-address-group") cfg_dict["network_group"] = self.parse_group_lst( conf, "network-group" ) + self.parse_group_lst(conf, "ipv6-network-group") return cfg_dict def parse_group_lst(self, conf, type, include_afi=True): """ This function fetches the name of group and invoke function to parse group attributes'. :param conf: configuration data. :param type: type of group. :param include_afi: if the afi should be included in the parsed object :return: generated group list configuration. """ g_lst = [] groups = findall(r"^set firewall group " + type + " (\\S+)", conf, M) if groups: rules_lst = [] for gr in set(groups): gr_regex = r" %s .+$" % gr cfg = "\n".join(findall(gr_regex, conf, M)) if "ipv6" in type: # fmt: off obj = self.parse_groups(cfg, type[len("ipv6-"):], gr) # fmt: on if include_afi: obj["afi"] = "ipv6" else: obj = self.parse_groups(cfg, type, gr) if include_afi: obj["afi"] = "ipv4" obj["name"] = gr.strip("'") if obj: rules_lst.append(obj) g_lst = sorted(rules_lst, key=lambda i: i["name"]) return g_lst def parse_groups(self, conf, type, name): """ This function fetches the description and invoke the parsing of group members. :param conf: configuration. :param type: type of group. :param name: name of group. :return: generated configuration dictionary. """ a_lst = ["name", "description"] group = self.parse_attr(conf, a_lst) key = self.get_key(type) r_sub = {key[0]: self.parse_address_port_lst(conf, name, key[1])} group.update(r_sub) return group def parse_address_port_lst(self, conf, name, key): """ This function forms the regex to fetch the group members attributes. :param conf: configuration data. :param name: name of group. :param key: key value. :return: generated member list configuration. """ l_lst = [] attribs = findall(r"^.*" + name + " " + key + " (\\S+)", conf, M) if attribs: for attr in attribs: if key == "port": l_lst.append({"port": attr.strip("'")}) else: l_lst.append({"address": attr.strip("'")}) return l_lst def parse_attr(self, conf, attr_list, match=None, type=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib, type) if match: regex = match + " " + regex if conf: if self.is_bool(attrib): attr = self.map_regex(attrib, type) out = conf.find(attr.replace("_", "-")) dis = conf.find(attr.replace("_", "-") + " 'disable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def get_key(self, type): """ This function map the group type to member type :param type: :return: """ key = () if type == "port-group": key = ("members", "port") elif type == "address-group": key = ("members", "address") elif type == "network-group": key = ("members", "network") return key def map_regex(self, attrib, type=None): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "all": regex = "all-ping" elif attrib == "disabled": regex = "disable" elif attrib == "broadcast": regex = "broadcast-ping" elif attrib == "send": if type == "ipv6": regex = "ipv6-send-redirects" else: regex = "send-redirects" elif attrib == "ip_src_route": if type == "ipv6": regex = "ipv6-src-route" elif attrib == "receive": if type == "ipv6": regex = "ipv6-receive-redirects" else: regex = "receive-redirects" return regex def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = ("time", "code", "type", "count", "burst", "number") return True if attrib in num_set else False def get_src_route(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ return "ipv6_src_route" if attrib == "ipv6" else "ip_src_route" def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "all", "log", "send", "receive", "broadcast", "config_trap", "log_martians", "syn_cookies", "ip_src_route", "twa_hazards_protection", ) return True if attrib in bool_set else False diff --git a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py index d0cfb53..3e0d323 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/firewall_interfaces/firewall_interfaces.py @@ -1,195 +1,187 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos firewall_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import ( Firewall_interfacesArgs, ) class Firewall_interfacesFacts(object): """The vyos firewall_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_interfacesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for firewall_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) objs = [] - interfaces = findall( - r"^set interfaces ethernet (?:\'*)(\S+)(?:\'*)", data, M - ) + interfaces = findall(r"^set interfaces ethernet (?:\'*)(\S+)(?:\'*)", data, M) if interfaces: objs = self.get_names(data, interfaces) - ansible_facts["ansible_network_resources"].pop( - "firewall_interfaces", None - ) + ansible_facts["ansible_network_resources"].pop("firewall_interfaces", None) facts = {} if objs: facts["firewall_interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) + params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["firewall_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def get_names(self, data, interfaces): """ This function performs following: - Form regex to fetch 'interface name' from interfaces firewall data. - Form the name list. :param data: configuration. :param rules: list of interfaces. :return: generated firewall interfaces configuration. """ names = [] for r in set(interfaces): int_regex = r" %s .+$" % r.strip("'") cfg = findall(int_regex, data, M) fi = self.render_config(cfg) fi["name"] = r.strip("'") names.append(fi) if names: names = sorted(names, key=lambda i: i["name"]) return names def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join(filter(lambda x: "firewall" in x, conf)) config = {"access_rules": self.parse_access_rules(conf)} return config def parse_access_rules(self, conf): """ This function forms the regex to fetch the 'access-rules' for specific interface. :param conf: configuration data. :return: generated access-rules list configuration. """ ar_lst = [] v4_ar = findall(r"^.*(in|out|local) name .*$", conf, M) v6_ar = findall(r"^.*(in|out|local) ipv6-name .*$", conf, M) if v4_ar: v4_conf = "\n".join(findall(r"(^.*?%s.*?$)" % " name", conf, M)) config = self.parse_int_rules(v4_conf, "ipv4") if config: ar_lst.append(config) if v6_ar: - v6_conf = "\n".join( - findall(r"(^.*?%s.*?$)" % " ipv6-name", conf, M) - ) + v6_conf = "\n".join(findall(r"(^.*?%s.*?$)" % " ipv6-name", conf, M)) config = self.parse_int_rules(v6_conf, "ipv6") if config: ar_lst.append(config) if ar_lst: ar_lst = sorted(ar_lst, key=lambda i: i["afi"]) else: empty_rules = findall(r"^.*(in|out|local).*", conf, M) if empty_rules: ar_lst.append({"afi": "ipv4", "rules": []}) ar_lst.append({"afi": "ipv6", "rules": []}) return ar_lst def parse_int_rules(self, conf, afi): """ This function forms the regex to fetch the 'access-rules' for specific interface based on ip-type. :param conf: configuration data. :param rules: rules configured per interface. :param afi: ip address type. :return: generated rule configuration dictionary. """ r_lst = [] config = {} rules = ["in", "out", "local"] for r in set(rules): fr = {} r_regex = r" %s .+$" % r cfg = "\n".join(findall(r_regex, conf, M)) if cfg: fr = self.parse_rules(cfg, afi) else: out = search(r"^.*firewall " + "'" + r + "'" + "(.*)", conf, M) if out: fr = {"direction": r} if fr: r_lst.append(fr) if r_lst: r_lst = sorted(r_lst, key=lambda i: i["direction"]) config = {"afi": afi, "rules": r_lst} return config def parse_rules(self, conf, afi): """ This function triggers the parsing of 'rule' attributes. a_lst is a list having rule attributes which doesn't have further sub attributes. :param conf: configuration. :param afi: ip address type. :return: generated rule configuration dictionary. """ cfg = {} out = findall(r"[^\s]+", conf, M) if out: cfg["direction"] = out[0].strip("'") if afi == "ipv6": out = findall(r"[^\s]+ ipv6-name (?:\'*)(\S+)(?:\'*)", conf, M) if out: cfg["name"] = str(out[0]).strip("'") else: out = findall(r"[^\s]+ name (?:\'*)(\S+)(?:\'*)", conf, M) if out: cfg["name"] = out[-1].strip("'") return cfg diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py index cb1d4bb..69daad9 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py @@ -1,390 +1,386 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos firewall_rules fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type import re -from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( Firewall_rulesArgs, ) class Firewall_rulesFacts(object): """The vyos firewall_rules fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_rulesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for firewall_rules :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) # split the config into instances of the resource objs = [] - v6_rules = findall( - r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M - ) + v6_rules = findall(r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M) v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) if v6_rules: config = self.get_rules(data, v6_rules, type="ipv6") if config: config = utils.remove_empties(config) objs.append(config) if v4_rules: config = self.get_rules(data, v4_rules, type="ipv4") if config: config = utils.remove_empties(config) objs.append(config) ansible_facts["ansible_network_resources"].pop("firewall_rules", None) facts = {} if objs: facts["firewall_rules"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) + params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["firewall_rules"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def get_rules(self, data, rules, type): """ This function performs following: - Form regex to fetch 'rule-sets' specific config from data. - Form the rule-set list based on ip address. :param data: configuration. :param rules: list of rule-sets. :param type: ip address type. :return: generated rule-sets configuration. """ r_v4 = [] r_v6 = [] for r in set(rules): name_key = "ipv6-name" if type == "ipv6" else "name" rule_regex = r" %s %s .+$" % (name_key, r.strip("'")) cfg = findall(rule_regex, data, M) fr = self.render_config(cfg, r.strip("'")) fr["name"] = r.strip("'") if type == "ipv6": r_v6.append(fr) else: r_v4.append(fr) if r_v4: config = {"afi": "ipv4", "rule_sets": r_v4} if r_v6: config = {"afi": "ipv6", "rule_sets": r_v6} return config def render_config(self, conf, match): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) a_lst = ["description", "default_action", "enable_default_log"] config = self.parse_attr(conf, a_lst, match) if not config: config = {} config["rules"] = self.parse_rules_lst(conf) return config def parse_rules_lst(self, conf): """ This function forms the regex to fetch the 'rules' with in 'rule-sets' :param conf: configuration data. :return: generated rule list configuration. """ r_lst = [] rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M) if rules: rules_lst = [] for r in set(rules): r_regex = r" %s .+$" % r cfg = "\n".join(findall(r_regex, conf, M)) obj = self.parse_rules(cfg) obj["number"] = int(r) if obj: rules_lst.append(obj) r_lst = sorted(rules_lst, key=lambda i: i["number"]) return r_lst def parse_rules(self, conf): """ This function triggers the parsing of 'rule' attributes. a_lst is a list having rule attributes which doesn't have further sub attributes. :param conf: configuration :return: generated rule configuration dictionary. """ a_lst = [ "ipsec", "log", "action", "protocol", "fragment", "disable", "description", "icmp", ] rule = self.parse_attr(conf, a_lst) r_sub = { "p2p": self.parse_p2p(conf), "tcp": self.parse_tcp(conf, "tcp"), "icmp": self.parse_icmp(conf, "icmp"), "time": self.parse_time(conf, "time"), "limit": self.parse_limit(conf, "limit"), "state": self.parse_state(conf, "state"), "recent": self.parse_recent(conf, "recent"), "source": self.parse_src_or_dest(conf, "source"), "destination": self.parse_src_or_dest(conf, "destination"), } rule.update(r_sub) return rule def parse_p2p(self, conf): """ This function forms the regex to fetch the 'p2p' with in 'rules' :param conf: configuration data. :return: generated rule list configuration. """ a_lst = [] applications = findall(r"p2p (?:\'*)(\d+)(?:\'*)", conf, M) if applications: app_lst = [] for r in set(applications): obj = {"application": r.strip("'")} app_lst.append(obj) a_lst = sorted(app_lst, key=lambda i: i["application"]) return a_lst def parse_src_or_dest(self, conf, attrib=None): """ This function triggers the parsing of 'source or destination' attributes. :param conf: configuration. :param attrib:'source/destination'. :return:generated source/destination configuration dictionary. """ a_lst = ["port", "address", "mac_address"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) cfg_dict["group"] = self.parse_group(conf, attrib + " group") return cfg_dict def parse_recent(self, conf, attrib=None): """ This function triggers the parsing of 'recent' attributes :param conf: configuration. :param attrib: 'recent'. :return: generated config dictionary. """ a_lst = ["time", "count"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_tcp(self, conf, attrib=None): """ This function triggers the parsing of 'tcp' attributes. :param conf: configuration. :param attrib: 'tcp'. :return: generated config dictionary. """ cfg_dict = self.parse_attr(conf, ["flags"], match=attrib) return cfg_dict def parse_time(self, conf, attrib=None): """ This function triggers the parsing of 'time' attributes. :param conf: configuration. :param attrib: 'time'. :return: generated config dictionary. """ a_lst = [ "stopdate", "stoptime", "weekdays", "monthdays", "startdate", "starttime", ] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_state(self, conf, attrib=None): """ This function triggers the parsing of 'state' attributes. :param conf: configuration :param attrib: 'state'. :return: generated config dictionary. """ a_lst = ["new", "invalid", "related", "established"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_group(self, conf, attrib=None): """ This function triggers the parsing of 'group' attributes. :param conf: configuration. :param attrib: 'group'. :return: generated config dictionary. """ a_lst = ["port_group", "address_group", "network_group"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_icmp(self, conf, attrib=None): """ This function triggers the parsing of 'icmp' attributes. :param conf: configuration to be parsed. :param attrib: 'icmp'. :return: generated config dictionary. """ a_lst = ["code", "type", "type_name"] if attrib == "icmp": attrib = "icmpv6" conf = re.sub("icmpv6 type", "icmpv6 type-name", conf) cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_limit(self, conf, attrib=None): """ This function triggers the parsing of 'limit' attributes. :param conf: configuration to be parsed. :param attrib: 'limit' :return: generated config dictionary. """ cfg_dict = self.parse_attr(conf, ["burst"], match=attrib) cfg_dict["rate"] = self.parse_rate(conf, "rate") return cfg_dict def parse_rate(self, conf, attrib=None): """ This function triggers the parsing of 'rate' attributes. :param conf: configuration. :param attrib: 'rate' :return: generated config dictionary. """ a_lst = ["unit", "number"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_attr(self, conf, attr_list, match=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib) if match: regex = match + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if not out and attrib == "disable": out = search(r"^.*\d+" + " ('disable'$)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) if attrib == "disable": val = True config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "disabled": regex = "disable" return regex def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "new", "invalid", "related", "disabled", "established", "enable_default_log", ) return True if attrib in bool_set else False def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = ("time", "code", "type", "count", "burst", "number") return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/facts/hostname/hostname.py b/plugins/module_utils/network/vyos/facts/hostname/hostname.py index acdddca..77ecc8c 100644 --- a/plugins/module_utils/network/vyos/facts/hostname/hostname.py +++ b/plugins/module_utils/network/vyos/facts/hostname/hostname.py @@ -1,76 +1,71 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos hostname fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +import re + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.hostname.hostname import ( + HostnameArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.hostname import ( HostnameTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.hostname.hostname import ( - HostnameArgs, -) - -import re class HostnameFacts(object): """The vyos hostname facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = HostnameArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep host-name") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Snmp_server network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_config(connection) for resource in data.splitlines(): config_lines.append(re.sub("'", "", resource)) # parse native config using the Hostname template - hostname_parser = HostnameTemplate( - lines=config_lines, module=self._module - ) + hostname_parser = HostnameTemplate(lines=config_lines, module=self._module) objs = hostname_parser.parse() ansible_facts["ansible_network_resources"].pop("hostname", None) params = utils.remove_empties( - hostname_parser.validate_config( - self.argument_spec, {"config": objs}, redact=True - ) + hostname_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) ) facts["hostname"] = params.get("config", {}) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py index ea3933c..c9479db 100644 --- a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py @@ -1,138 +1,134 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( InterfacesArgs, ) class InterfacesFacts(object): """The vyos interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = InterfacesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): data = connection.get_config(flags=["| grep interfaces"]) return data def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) objs = [] interface_names = findall( r"^set interfaces (?:ethernet|bonding|vti|loopback|vxlan|openvpn|wireguard) (?:\'*)(\S+)(?:\'*)", data, M, ) if interface_names: for interface in set(interface_names): intf_regex = r" %s .+$" % interface.strip("'") cfg = findall(intf_regex, data, M) obj = self.render_config(cfg) obj["name"] = interface.strip("'") if obj: objs.append(obj) facts = {} if objs: facts["interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) + params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) - config = self.parse_attribs( - ["description", "speed", "mtu", "duplex"], eth_conf - ) + config = self.parse_attribs(["description", "speed", "mtu", "duplex"], eth_conf) config["vifs"] = self.parse_vifs(vif_conf) return utils.remove_empties(config) def parse_vifs(self, conf): vif_names = findall(r"vif (?:\'*)(\d+)(?:\'*)", conf, M) vifs_list = None if vif_names: vifs_list = [] for vif in set(vif_names): vif_regex = r" %s .+$" % vif cfg = "\n".join(findall(vif_regex, conf, M)) obj = self.parse_attribs(["description", "mtu"], cfg) obj["vlan_id"] = int(vif) if obj: vifs_list.append(obj) vifs_list = sorted(vifs_list, key=lambda i: i["vlan_id"]) return vifs_list def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value and item == "mtu": config[item] = int(value.strip("'")) elif value: config[item] = value.strip("'") else: config[item] = None if "disable" in conf: config["enabled"] = False else: config["enabled"] = True return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py index 09ab994..ea4cf69 100644 --- a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py @@ -1,142 +1,140 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos l3_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type import re from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) + from ansible.module_utils.six import iteritems -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - get_ip_address_version, -) +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( L3_interfacesArgs, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( + get_ip_address_version, +) class L3_interfacesFacts(object): """The vyos l3_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = L3_interfacesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for l3_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get_config() # operate on a collection of resource x objs = [] interface_names = re.findall( r"set interfaces (?:ethernet|bonding|vti|vxlan) (?:\'*)(\S+)(?:\'*)", data, re.M, ) if interface_names: for interface in set(interface_names): intf_regex = r" %s .+$" % interface cfg = re.findall(intf_regex, data, re.M) obj = self.render_config(cfg) obj["name"] = interface.strip("'") if obj: objs.append(obj) ansible_facts["ansible_network_resources"].pop("l3_interfaces", None) facts = {} if objs: facts["l3_interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) + params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["l3_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) config = self.parse_attribs(eth_conf) config["vifs"] = self.parse_vifs(vif_conf) return utils.remove_empties(config) def parse_vifs(self, conf): vif_names = re.findall(r"vif (\d+)", conf, re.M) vifs_list = None if vif_names: vifs_list = [] for vif in set(vif_names): vif_regex = r" %s .+$" % vif cfg = "\n".join(re.findall(vif_regex, conf, re.M)) obj = self.parse_attribs(cfg) obj["vlan_id"] = vif if obj: vifs_list.append(obj) return vifs_list def parse_attribs(self, conf): config = {} ipaddrs = re.findall(r"address (\S+)", conf, re.M) config["ipv4"] = [] config["ipv6"] = [] for item in ipaddrs: item = item.strip("'") if item == "dhcp": config["ipv4"].append({"address": item}) elif item == "dhcpv6": config["ipv6"].append({"address": item}) else: ip_version = get_ip_address_version(item.split("/")[0]) if ip_version == 4: config["ipv4"].append({"address": item}) else: config["ipv6"].append({"address": item}) for key, value in iteritems(config): if value == []: config[key] = None return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py index ff02cb0..251178a 100644 --- a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py @@ -1,143 +1,136 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos lag_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, search, M from copy import deepcopy +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( Lag_interfacesArgs, ) class Lag_interfacesFacts(object): """The vyos lag_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lag_interfacesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lag_interfaces :param module: the module instance :param connection: the device connection :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get_config() objs = [] lag_names = findall(r"^set interfaces bonding (\S+)", data, M) if lag_names: for lag in set(lag_names): lag_regex = r" %s .+$" % lag cfg = findall(lag_regex, data, M) obj = self.render_config(cfg) members = [] member = {} group_regex = r".*eth.* '%s'" % lag g_cfg = findall(group_regex, data, M) for item in g_cfg: output = search("^set interfaces ethernet (\\S+)", item, M) if output: member["member"] = output.group(1).strip("'") members.append(member) obj["name"] = lag.strip("'") if members: obj["members"] = members if obj: objs.append(obj) facts = {} if objs: facts["lag_interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) + params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["lag_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ - arp_monitor_conf = "\n".join( - filter(lambda x: ("arp-monitor" in x), conf) - ) - hash_policy_conf = "\n".join( - filter(lambda x: ("hash-policy" in x), conf) - ) + arp_monitor_conf = "\n".join(filter(lambda x: ("arp-monitor" in x), conf)) + hash_policy_conf = "\n".join(filter(lambda x: ("hash-policy" in x), conf)) lag_conf = "\n".join(filter(lambda x: ("bond" in x), conf)) config = self.parse_attribs(["mode", "primary"], lag_conf) config["arp_monitor"] = self.parse_arp_monitor(arp_monitor_conf) config["hash_policy"] = self.parse_hash_policy(hash_policy_conf) return utils.remove_empties(config) def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: config[item] = value.strip("'") else: config[item] = None return utils.remove_empties(config) def parse_arp_monitor(self, conf): arp_monitor = None if conf: arp_monitor = {} target_list = [] interval = search(r"^.*arp-monitor interval (.+)", conf, M) targets = findall(r"^.*arp-monitor target '(.+)'", conf, M) if targets: for target in targets: target_list.append(target) arp_monitor["target"] = target_list if interval: value = interval.group(1).strip("'") arp_monitor["interval"] = int(value) return arp_monitor def parse_hash_policy(self, conf): hash_policy = None if conf: hash_policy = search(r"^.*hash-policy (.+)", conf, M) hash_policy = hash_policy.group(1).strip("'") return hash_policy diff --git a/plugins/module_utils/network/vyos/facts/legacy/base.py b/plugins/module_utils/network/vyos/facts/legacy/base.py index 6f063d6..719df3f 100644 --- a/plugins/module_utils/network/vyos/facts/legacy/base.py +++ b/plugins/module_utils/network/vyos/facts/legacy/base.py @@ -1,162 +1,163 @@ # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The VyOS interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type import platform import re + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - run_commands, get_capabilities, + run_commands, ) class LegacyFactsBase(object): COMMANDS = frozenset() def __init__(self, module): self.module = module self.facts = dict() self.warnings = list() self.responses = None def populate(self): self.responses = run_commands(self.module, list(self.COMMANDS)) class Default(LegacyFactsBase): COMMANDS = [ "show version", ] def populate(self): super(Default, self).populate() data = self.responses[0] self.facts["serialnum"] = self.parse_serialnum(data) self.facts.update(self.platform_facts()) def parse_serialnum(self, data): match = re.search(r"(?:HW|Hardware) S/N:\s+(\S+)", data) if match: return match.group(1) def platform_facts(self): platform_facts = {} resp = get_capabilities(self.module) device_info = resp["device_info"] platform_facts["system"] = device_info["network_os"] for item in ("model", "image", "version", "platform", "hostname"): val = device_info.get("network_os_%s" % item) if val: platform_facts[item] = val platform_facts["api"] = resp["network_api"] platform_facts["python_version"] = platform.python_version() return platform_facts class Config(LegacyFactsBase): COMMANDS = [ "show configuration commands", "show system commit", ] def populate(self): super(Config, self).populate() self.facts["config"] = self.responses commits = self.responses[1] entries = list() entry = None for line in commits.split("\n"): match = re.match(r"(\d+)\s+(.+)by(.+)via(.+)", line) if match: if entry: entries.append(entry) entry = dict( revision=match.group(1), datetime=match.group(2), by=str(match.group(3)).strip(), via=str(match.group(4)).strip(), comment=None, ) else: entry["comment"] = line.strip() self.facts["commits"] = entries class Neighbors(LegacyFactsBase): COMMANDS = [ "show lldp neighbors", "show lldp neighbors detail", ] def populate(self): super(Neighbors, self).populate() all_neighbors = self.responses[0] if "LLDP not configured" not in all_neighbors: neighbors = self.parse(self.responses[1]) self.facts["neighbors"] = self.parse_neighbors(neighbors) def parse(self, data): parsed = list() values = None for line in data.split("\n"): if not line: continue if line[0] == " ": values += "\n%s" % line elif line.startswith("Interface"): if values: parsed.append(values) values = line if values: parsed.append(values) return parsed def parse_neighbors(self, data): facts = dict() for item in data: interface = self.parse_interface(item) host = self.parse_host(item) port = self.parse_port(item) if interface not in facts: facts[interface] = list() facts[interface].append(dict(host=host, port=port)) return facts def parse_interface(self, data): match = re.search(r"^Interface:\s+(\S+),", data) return match.group(1) def parse_host(self, data): match = re.search(r"SysName:\s+(.+)$", data, re.M) if match: return match.group(1) def parse_port(self, data): match = re.search(r"PortDescr:\s+(.+)$", data, re.M) if match: return match.group(1) diff --git a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py index 22b6bc8..e563999 100644 --- a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py @@ -1,115 +1,110 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos lldp_global fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, M from copy import deepcopy +from re import M, findall + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import ( Lldp_globalArgs, ) class Lldp_globalFacts(object): """The vyos lldp_global fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lldp_globalArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lldp_global :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get_config() objs = {} lldp_output = findall(r"^set service lldp (\S+)", data, M) if lldp_output: for item in set(lldp_output): lldp_regex = r" %s .+$" % item cfg = findall(lldp_regex, data, M) obj = self.render_config(cfg) if obj: objs.update(obj) lldp_service = findall(r"^set service (lldp)?('lldp')", data, M) if lldp_service or lldp_output: lldp_obj = {} lldp_obj["enable"] = True objs.update(lldp_obj) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["lldp_global"] = utils.remove_empties(params["config"]) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ - protocol_conf = "\n".join( - filter(lambda x: ("legacy-protocols" in x), conf) - ) - att_conf = "\n".join( - filter(lambda x: ("legacy-protocols" not in x), conf) - ) + protocol_conf = "\n".join(filter(lambda x: ("legacy-protocols" in x), conf)) + att_conf = "\n".join(filter(lambda x: ("legacy-protocols" not in x), conf)) config = self.parse_attribs(["snmp", "address"], att_conf) config["legacy_protocols"] = self.parse_protocols(protocol_conf) return utils.remove_empties(config) def parse_protocols(self, conf): protocol_support = None if conf: protocols = findall(r"^.*legacy-protocols (.+)", conf, M) if protocols: protocol_support = [] for protocol in protocols: protocol_support.append(protocol.strip("'")) return protocol_support def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: config[item] = value.strip("'") else: config[item] = None return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py index 64d6a79..bfe684f 100644 --- a/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py @@ -1,154 +1,149 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos lldp_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, search, M from copy import deepcopy +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesArgs, ) class Lldp_interfacesFacts(object): """The vyos lldp_interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lldp_interfacesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lldp_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get_config() objs = [] lldp_names = findall(r"^set service lldp interface (\S+)", data, M) if lldp_names: for lldp in set(lldp_names): lldp_regex = r" %s .+$" % lldp cfg = findall(lldp_regex, data, M) obj = self.render_config(cfg) obj["name"] = lldp.strip("'") if obj: objs.append(obj) facts = {} if objs: facts["lldp_interfaces"] = objs ansible_facts["ansible_network_resources"].update(facts) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = {} location = {} civic_conf = "\n".join(filter(lambda x: ("civic-based" in x), conf)) elin_conf = "\n".join(filter(lambda x: ("elin" in x), conf)) - coordinate_conf = "\n".join( - filter(lambda x: ("coordinate-based" in x), conf) - ) + coordinate_conf = "\n".join(filter(lambda x: ("coordinate-based" in x), conf)) disable = "\n".join(filter(lambda x: ("disable" in x), conf)) coordinate_based_conf = self.parse_attribs( ["altitude", "datum", "longitude", "latitude"], coordinate_conf ) elin_based_conf = self.parse_lldp_elin_based(elin_conf) civic_based_conf = self.parse_lldp_civic_based(civic_conf) if disable: config["enable"] = False if coordinate_conf: location["coordinate_based"] = coordinate_based_conf config["location"] = location elif civic_based_conf: location["civic_based"] = civic_based_conf config["location"] = location elif elin_conf: location["elin"] = elin_based_conf config["location"] = location return utils.remove_empties(config) def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: value = value.strip("'") if item == "altitude": value = int(value) config[item] = value else: config[item] = None return utils.remove_empties(config) def parse_lldp_civic_based(self, conf): civic_based = None if conf: civic_info_list = [] civic_add_list = findall(r"^.*civic-based ca-type (.+)", conf, M) if civic_add_list: for civic_add in civic_add_list: ca = civic_add.split(" ") c_add = {} c_add["ca_type"] = int(ca[0].strip("'")) c_add["ca_value"] = ca[2].strip("'") civic_info_list.append(c_add) - country_code = search( - r"^.*civic-based country-code (.+)", conf, M - ) + country_code = search(r"^.*civic-based country-code (.+)", conf, M) civic_based = {} civic_based["ca_info"] = civic_info_list civic_based["country_code"] = country_code.group(1).strip("'") return civic_based def parse_lldp_elin_based(self, conf): elin_based = None if conf: e_num = search(r"^.* elin (.+)", conf, M) elin_based = e_num.group(1).strip("'") return elin_based diff --git a/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py b/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py index 5f9d65d..e0434d9 100644 --- a/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py +++ b/plugins/module_utils/network/vyos/facts/logging_global/logging_global.py @@ -1,107 +1,96 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos logging_global fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.logging_global.logging_global import ( + Logging_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.logging_global import ( Logging_globalTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.logging_global.logging_global import ( - Logging_globalArgs, -) class Logging_globalFacts(object): """The vyos logging_global facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Logging_globalArgs.argument_spec def get_logging_data(self, connection): return connection.get("show configuration commands | grep syslog") def process_facts(self, objFinal): if objFinal: for ke, vl in iteritems(objFinal): if ke == "files": _files = [] for k, v in vl.items(): _files.append(v) objFinal[ke] = _files - objFinal[ke] = sorted( - objFinal[ke], key=lambda item: item["path"] - ) + objFinal[ke] = sorted(objFinal[ke], key=lambda item: item["path"]) elif ke == "hosts": _hosts = [] for k, v in vl.items(): _hosts.append(v) objFinal[ke] = _hosts - objFinal[ke] = sorted( - objFinal[ke], key=lambda item: item["hostname"] - ) + objFinal[ke] = sorted(objFinal[ke], key=lambda item: item["hostname"]) elif ke == "users": _users = [] for k, v in vl.items(): _users.append(v) objFinal[ke] = _users - objFinal[ke] = sorted( - objFinal[ke], key=lambda item: item["username"] - ) + objFinal[ke] = sorted(objFinal[ke], key=lambda item: item["username"]) elif ke == "console" or ke == "global_params": if objFinal[ke].get("facilities"): objFinal[ke]["facilities"] = sorted( objFinal[ke]["facilities"], key=lambda item: item["facility"], ) return objFinal def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Logging_global network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if not data: data = self.get_logging_data(connection) # parse native config using the Logging_global template - logging_global_parser = Logging_globalTemplate( - lines=data.splitlines(), module=self._module - ) + logging_global_parser = Logging_globalTemplate(lines=data.splitlines(), module=self._module) objs = logging_global_parser.parse() ansible_facts["ansible_network_resources"].pop("logging_global", None) objs = self.process_facts(objs) params = utils.remove_empties( - logging_global_parser.validate_config( - self.argument_spec, {"config": objs}, redact=True - ) + logging_global_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) ) facts["logging_global"] = params.get("config", {}) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py index bb20e2d..cead241 100644 --- a/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/facts/ntp_global/ntp_global.py @@ -1,99 +1,91 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function - __metaclass__ = type """ The vyos ntp fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ntp_global.ntp_global import ( + Ntp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ntp_global import ( NtpTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ntp_global.ntp_global import ( - Ntp_globalArgs, -) class Ntp_globalFacts(object): """The vyos ntp facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Ntp_globalArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep ntp") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Ntp network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_config(connection) for resource in data.splitlines(): config_lines.append(re.sub("'", "", resource)) # parse native config using the Ntp template ntp_parser = NtpTemplate(lines=config_lines, module=self._module) objs = ntp_parser.parse() if objs: if "allow_clients" in objs: objs["allow_clients"] = sorted(list(objs["allow_clients"])) if "listen_addresses" in objs: - objs["listen_addresses"] = sorted( - list(objs["listen_addresses"]) - ) + objs["listen_addresses"] = sorted(list(objs["listen_addresses"])) """ if "options" in objs["servers"].values(): val = objs["servers"].values() val["options"] = sorted(val["options"]) """ if "servers" in objs: objs["servers"] = list(objs["servers"].values()) - objs["servers"] = sorted( - objs["servers"], key=lambda k: k["server"] - ) + objs["servers"] = sorted(objs["servers"], key=lambda k: k["server"]) for i in objs["servers"]: if "options" in i: i["options"] = sorted(list(i["options"])) ansible_facts["ansible_network_resources"].pop("ntp_global", None) params = utils.remove_empties( - ntp_parser.validate_config( - self.argument_spec, {"config": objs}, redact=True - ) + ntp_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) ) if params.get("config"): facts["ntp_global"] = params["config"] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py index 4057db5..c89e2bd 100644 --- a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py @@ -1,106 +1,101 @@ # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos ospf_interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ import re -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( + Ospf_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import ( Ospf_interfacesTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( - Ospf_interfacesArgs, -) class Ospf_interfacesFacts(object): """The vyos ospf_interfaces facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Ospf_interfacesArgs.argument_spec def get_device_data(self, connection): - return connection.get( - 'show configuration commands | match "set interfaces"' - ) + return connection.get('show configuration commands | match "set interfaces"') def get_config_set(self, data): """To classify the configurations beased on interface""" interface_list = [] config_set = [] int_string = "" for config_line in data.splitlines(): ospf_int = re.search(r"set interfaces \S+ (\S+) .*", config_line) if ospf_int: if ospf_int.group(1) not in interface_list: if int_string: config_set.append(int_string) interface_list.append(ospf_int.group(1)) int_string = "" int_string = int_string + config_line + "\n" if int_string: config_set.append(int_string) return config_set def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Ospf_interfaces network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] - ospf_interfaces_parser = Ospf_interfacesTemplate( - lines=[], module=self._module - ) + ospf_interfaces_parser = Ospf_interfacesTemplate(lines=[], module=self._module) if not data: data = self.get_device_data(connection) # parse native config using the Ospf_interfaces template ospf_interfaces_facts = [] resources = self.get_config_set(data) for resource in resources: ospf_interfaces_parser = Ospf_interfacesTemplate( lines=resource.split("\n"), module=self._module ) objs = ospf_interfaces_parser.parse() for key, sortv in [("address_family", "afi")]: if key in objs and objs[key]: objs[key] = list(objs[key].values()) ospf_interfaces_facts.append(objs) ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None) facts = {"ospf_interfaces": []} params = utils.remove_empties( ospf_interfaces_parser.validate_config( self.argument_spec, {"config": ospf_interfaces_facts}, redact=True, ) ) if params.get("config"): for cfg in params["config"]: facts["ospf_interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py index b0c9df2..4dcd546 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -1,501 +1,482 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos ospfv2 fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( Ospfv2Args, ) class Ospfv2Facts(object): """The vyos ospfv2 fact class""" def __init__( self, module, subspec="config", options="options", ): self._module = module self.argument_spec = Ospfv2Args.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for ospfv2 :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead objs = {} ospfv2 = findall(r"^set protocols ospf (.+)", data, M) if ospfv2: objs = self.render_config(ospfv2) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["ospfv2"] = utils.remove_empties(params["config"]) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure :param conf: The configuration :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) a_lst = ["default_metric", "log_adjacency_changes"] config = self.parse_attr(conf, a_lst) if not config: config = {} config["timers"] = self.parse_timers(conf) config["auto_cost"] = self.parse_auto_cost(conf) config["distance"] = self.parse_distance(conf) config["max_metric"] = self.parse_max_metric(conf) config["default_information"] = self.parse_def_info(conf) config["route_map"] = self.parse_leaf_list(conf, "route-map") config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te") config["areas"] = self.parse_attrib_list(conf, "area", "area_id") - config["parameters"] = self.parse_attrib( - conf, "parameters", "parameters" - ) - config["neighbor"] = self.parse_attrib_list( - conf, "neighbor", "neighbor_id" - ) - config["passive_interface"] = self.parse_leaf_list( - conf, "passive-interface" - ) - config["redistribute"] = self.parse_attrib_list( - conf, "redistribute", "route_type" - ) + config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") + config["neighbor"] = self.parse_attrib_list(conf, "neighbor", "neighbor_id") + config["passive_interface"] = self.parse_leaf_list(conf, "passive-interface") + config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") config["passive_interface_exclude"] = self.parse_leaf_list( conf, "passive-interface-exclude" ) return config def parse_timers(self, conf): """ This function triggers the parsing of 'timers' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["refresh"] = self.parse_refresh(conf, "refresh") cfg_dict["throttle"] = self.parse_throttle(conf, "spf") return cfg_dict def parse_throttle(self, conf, attrib=None): """ This function triggers the parsing of 'throttle' attributes :param conf: configuration :param attrib: 'spf' :return: generated config dictionary """ cfg_dict = {} cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib) return cfg_dict def parse_refresh(self, conf, attrib=None): """ This function triggers the parsing of 'refresh' attributes :param conf: configuration :param attrib: 'refresh' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["timers"], match=attrib) return cfg_dict def parse_leaf_list(self, conf, attrib): """ This function forms the regex to fetch the listed attributes from the configuration data :param conf: configuration data :param attrib: attribute name :return: generated rule list configuration """ lst = [] items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: for i in set(items): lst.append(i.strip("'")) lst.sort() return lst def parse_distance(self, conf, attrib=None): """ This function triggers the parsing of 'distance' attributes :param conf: configuration :param attrib: attribute name :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["global"], match=attrib) cfg_dict["ospf"] = self.parse_ospf(conf, "ospf") return cfg_dict def parse_ospf(self, conf, attrib=None): """ This function triggers the parsing of 'distance ospf' attributes :param conf: configuration :param attrib: 'ospf' :return: generated config dictionary """ cfg_dict = self.parse_attrib(conf, "ospf", match=attrib) return cfg_dict def parse_max_metric(self, conf): """ This function triggers the parsing of 'max_metric' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} - cfg_dict["router_lsa"] = self.parse_attrib( - conf, "router_lsa", match="router-lsa" - ) + cfg_dict["router_lsa"] = self.parse_attrib(conf, "router_lsa", match="router-lsa") return cfg_dict def parse_auto_cost(self, conf, attrib=None): """ This function triggers the parsing of 'auto_cost' attributes :param conf: configuration :param attrib: attribute name :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib) return cfg_dict def parse_def_info(self, conf): """ This function triggers the parsing of 'default_information' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} - cfg_dict["originate"] = self.parse_attrib( - conf, "originate", "originate" - ) + cfg_dict["originate"] = self.parse_attrib(conf, "originate", "originate") return cfg_dict def parse_area(self, conf, area_id): """ This function triggers the parsing of 'area' attributes. :param conf: configuration data :param area_id: area identity :return: generated rule configuration dictionary. """ rule = self.parse_attrib(conf, "area_id", match=area_id) r_sub = { "area_type": self.parse_area_type(conf, "area-type"), "network": self.parse_network(conf), "range": self.parse_attrib_list(conf, "range", "address"), - "virtual_link": self.parse_attrib_list( - conf, "virtual-link", "address" - ), + "virtual_link": self.parse_attrib_list(conf, "virtual-link", "address"), } rule.update(r_sub) return rule def parse_key(self, conf, key_id): """ This function triggers the parsing of 'area' attributes. :param conf: configuration data :param area_id: area identity :return: generated rule configuration dictionary. """ rule = self.parse_attrib(conf, "key_id", match=key_id) return rule def parse_area_type(self, conf, attrib=None): """ This function triggers the parsing of 'area_type' attributes :param conf: configuration :param attrib: 'area-type' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["normal"], match=attrib) cfg_dict["nssa"] = self.parse_attrib(conf, "nssa", match="nssa") cfg_dict["stub"] = self.parse_attrib(conf, "stub", match="stub") return cfg_dict def parse_network(self, conf): """ This function forms the regex to fetch the 'network' :param conf: configuration data :return: generated rule list configuration """ a_lst = [] applications = findall(r"network (.+)", conf, M) if applications: app_lst = [] for r in set(applications): obj = {"address": r.strip("'")} app_lst.append(obj) a_lst = sorted(app_lst, key=lambda i: i["address"]) return a_lst def parse_vlink(self, conf): """ This function triggers the parsing of 'virtual_link' attributes :param conf: configuration data :return: generated rule configuration dictionary """ rule = self.parse_attrib(conf, "vlink") - r_sub = { - "authentication": self.parse_authentication(conf, "authentication") - } + r_sub = {"authentication": self.parse_authentication(conf, "authentication")} rule.update(r_sub) return rule def parse_authentication(self, conf, attrib=None): """ This function triggers the parsing of 'authentication' attributes. :param conf: configuration :param attrib: 'authentication' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib) cfg_dict["md5"] = self.parse_attrib_list(conf, "key-id", "key_id") return cfg_dict def parse_attrib_list(self, conf, attrib, param): """ This function forms the regex to fetch the listed attributes from config :param conf: configuration data :param attrib: attribute name :param param: parameter data :return: generated rule list configuration """ r_lst = [] if attrib == "area": items = findall( r"^" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)", conf, M, ) elif attrib == "key-id": items = findall( r"^.*" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)", conf, M, ) else: items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: a_lst = [] for item in set(items): i_regex = r" %s .+$" % item cfg = "\n".join(findall(i_regex, conf, M)) if attrib == "area": obj = self.parse_area(cfg, item) elif attrib == "virtual-link": obj = self.parse_vlink(cfg) elif attrib == "key-id": obj = self.parse_key(cfg, item) else: obj = self.parse_attrib(cfg, attrib) obj[param] = item.strip("'") if obj: a_lst.append(obj) r_lst = sorted(a_lst, key=lambda i: i[param]) return r_lst def parse_attrib(self, conf, param, match=None): """ This function triggers the parsing of 'ospf' attributes :param conf: configuration data :return: generated configuration dictionary """ param_lst = { "key_id": ["md5_key"], "mpls_te": ["enabled", "router_address"], "area_id": ["shortcut", "authentication"], "neighbor": ["priority", "poll_interval"], "stub": ["set", "default_cost", "no_summary"], "range": ["cost", "substitute", "not_advertise"], "ospf": ["external", "inter_area", "intra_area"], "spf": ["delay", "max_holdtime", "initial_holdtime"], "redistribute": ["metric", "metric_type", "route_map"], "nssa": ["set", "translate", "default_cost", "no_summary"], "config_routes": ["default_metric", "log_adjacency_changes"], "originate": ["always", "metric", "metric_type", "route_map"], "router_lsa": ["administrative", "on_shutdown", "on_startup"], "parameters": [ "abr_type", "opaque_lsa", "router_id", "rfc1583_compatibility", ], "vlink": [ "dead_interval", "hello_interval", "transmit_delay", "retransmit_interval", ], } cfg_dict = self.parse_attr(conf, param_lst[param], match) return cfg_dict def parse_attr(self, conf, attr_list, match=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib) if match: regex = match.replace("_", "-") + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if match: if attrib == "set" and conf.find(match) >= 1: config[attrib] = True en = conf.find(match + " 'enable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True elif match and en >= 1: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return ( "disable" if attrib == "disabled" else ( "enable" if attrib == "enabled" - else ( - "area" if attrib == "area_id" else attrib.replace("_", "-") - ) + else ("area" if attrib == "area_id" else attrib.replace("_", "-")) ) ) def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "set", "always", "normal", "enabled", "opaque_lsa", "not_advertise", "administrative", "rfc1583_compatibility", ) return True if attrib in bool_set else False def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = ( "ospf", "delay", "metric", "inter_area", "intra_area", "on_startup", "metric_type", "on_shutdown", "max_holdtime", "poll_interval", "default_metric", "initial_holdtime", "key_id", ) return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py index 414c65e..121a348 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py +++ b/plugins/module_utils/network/vyos/facts/ospfv3/ospfv3.py @@ -1,212 +1,208 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos ospfv3 fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import ( Ospfv3Args, ) class Ospfv3Facts(object): """The vyos ospfv3 fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Ospfv3Args.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for ospfv3 :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead objs = {} ospfv3 = findall(r"^set protocols ospfv3 (.+)", data, M) if ospfv3: objs = self.render_config(ospfv3) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["ospfv3"] = utils.remove_empties(params["config"]) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure :param conf: The configuration :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) config = {} - config["parameters"] = self.parse_attrib( - conf, "parameters", "parameters" - ) + config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") config["areas"] = self.parse_attrib_list(conf, "area", "area_id") - config["redistribute"] = self.parse_attrib_list( - conf, "redistribute", "route_type" - ) + config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") return config def parse_attrib_list(self, conf, attrib, param): """ This function forms the regex to fetch the listed attributes from config :param conf: configuration data :param attrib: attribute name :param param: parameter data :return: generated rule list configuration """ r_lst = [] if attrib == "area": items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) else: items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: a_lst = [] for item in set(items): i_regex = r" %s .+$" % item cfg = "\n".join(findall(i_regex, conf, M)) if attrib == "area": obj = self.parse_area(cfg, item) else: obj = self.parse_attrib(cfg, attrib) obj[param] = item.strip("'") if obj: a_lst.append(obj) r_lst = sorted(a_lst, key=lambda i: i[param]) return r_lst def parse_area(self, conf, area_id): """ This function triggers the parsing of 'area' attributes. :param conf: configuration data :param area_id: area identity :return: generated rule configuration dictionary. """ rule = self.parse_attrib(conf, "area_id", match=area_id) r_sub = {"range": self.parse_attrib_list(conf, "range", "address")} rule.update(r_sub) return rule def parse_attrib(self, conf, param, match=None): """ This function triggers the parsing of 'ospf' attributes :param conf: configuration data :return: generated configuration dictionary """ param_lst = { "area_id": ["export_list", "import_list"], "redistribute": ["route_map"], "range": ["advertise", "not_advertise"], "parameters": ["router_id"], } cfg_dict = self.parse_attr(conf, param_lst[param], match) return cfg_dict def parse_attr(self, conf, attr_list, match=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib) if match: regex = match.replace("_", "-") + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if match: en = conf.find(match + " 'enable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True elif match and en >= 1: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return ( "disable" if attrib == "disabled" else "enable" if attrib == "enabled" else attrib.replace("_", "-") ) def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ("enabled", "advertise", "not_advertise") return True if attrib in bool_set else False def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = "ospf" return True if attrib in num_set else False diff --git a/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py b/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py index 15a2db9..19004c8 100644 --- a/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py +++ b/plugins/module_utils/network/vyos/facts/prefix_lists/prefix_lists.py @@ -1,93 +1,88 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos prefix_lists fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.prefix_lists.prefix_lists import ( + Prefix_listsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.prefix_lists import ( Prefix_listsTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.prefix_lists.prefix_lists import ( - Prefix_listsArgs, -) class Prefix_listsFacts(object): """The vyos prefix_lists facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Prefix_listsArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep prefix-list") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Prefix_lists network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if not data: data = self.get_config(connection) # parse native config using the Prefix_lists template - prefix_lists_parser = Prefix_listsTemplate( - lines=data.splitlines(), module=self._module - ) + prefix_lists_parser = Prefix_listsTemplate(lines=data.splitlines(), module=self._module) objs = prefix_lists_parser.parse() objs = sorted( list(objs.values()), key=lambda k: k["afi"], ) if objs: for item in objs: item["prefix_lists"] = sorted( list(item["prefix_lists"].values()), key=lambda k: k["name"], ) for pl in item["prefix_lists"]: if "entries" in pl: pl["entries"] = sorted( list(pl["entries"].values()), key=lambda k: k["sequence"], ) ansible_facts["ansible_network_resources"].pop("prefix_lists", None) params = utils.remove_empties( - prefix_lists_parser.validate_config( - self.argument_spec, {"config": objs}, redact=True - ) + prefix_lists_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) ) if params.get("config"): facts["prefix_lists"] = params["config"] else: facts["prefix_lists"] = [] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py index 51e8f97..9f53264 100644 --- a/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py +++ b/plugins/module_utils/network/vyos/facts/route_maps/route_maps.py @@ -1,84 +1,81 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos route_maps fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.route_maps.route_maps import ( + Route_mapsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps import ( Route_mapsTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.route_maps.route_maps import ( - Route_mapsArgs, -) class Route_mapsFacts(object): """The vyos route_maps facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Route_mapsArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_config(self, connection): return connection.get("show configuration commands | grep route-map") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Route_maps network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] if not data: data = self.get_config(connection) # parse native config using the Route_maps template route_maps_parser = Route_mapsTemplate(lines=data.splitlines()) if route_maps_parser.parse().get("route_maps"): objs = list(route_maps_parser.parse().get("route_maps").values()) for item in objs: if item.get("entries"): item["entries"] = list(item["entries"].values()) ansible_facts["ansible_network_resources"].pop("route_maps", None) # import epdb;epdb.serve() - params = utils.remove_empties( - utils.validate_config(self.argument_spec, {"config": objs}) - ) + params = utils.remove_empties(utils.validate_config(self.argument_spec, {"config": objs})) if params.get("config"): facts["route_maps"] = params["config"] ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py b/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py index 2a9ba48..54339f5 100644 --- a/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py +++ b/plugins/module_utils/network/vyos/facts/snmp_server/snmp_server.py @@ -1,117 +1,113 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos snmp_server fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ +import re + from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.snmp_server.snmp_server import ( + Snmp_serverArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.snmp_server import ( Snmp_serverTemplate, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.snmp_server.snmp_server import ( - Snmp_serverArgs, -) -import re class Snmp_serverFacts(object): """The vyos snmp_server facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Snmp_serverArgs.argument_spec def get_config(self, connection): return connection.get("show configuration commands | grep snmp") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Snmp_server network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = [] config_lines = [] if not data: data = self.get_config(connection) for resource in data.splitlines(): config_lines.append(re.sub("'", "", resource)) # parse native config using the Snmp_server template - snmp_server_parser = Snmp_serverTemplate( - lines=config_lines, module=self._module - ) + snmp_server_parser = Snmp_serverTemplate(lines=config_lines, module=self._module) objs = snmp_server_parser.parse() if objs: if "communities" in objs: for k in objs["communities"].values(): for param, val in iteritems(k): if param in ["clients", "networks"]: if None in val: val.remove(None) val.sort() objs["communities"] = sorted( list(objs["communities"].values()), key=lambda k, sk="name": k[sk], ) if "listen_addresses" in objs: objs["listen_addresses"] = sorted( list(objs["listen_addresses"].values()), key=lambda k, sk="address": k[sk], ) if "snmp_v3" in objs: if "groups" in objs["snmp_v3"]: objs["snmp_v3"]["groups"] = sorted( list(objs["snmp_v3"]["groups"].values()), key=lambda k, sk="group": k[sk], ) if "trap_targets" in objs["snmp_v3"]: objs["snmp_v3"]["trap_targets"] = sorted( list(objs["snmp_v3"]["trap_targets"].values()), key=lambda k, sk="address": k[sk], ) if "users" in objs["snmp_v3"]: objs["snmp_v3"]["users"] = sorted( list(objs["snmp_v3"]["users"].values()), key=lambda k, sk="user": k[sk], ) if "views" in objs["snmp_v3"]: objs["snmp_v3"]["views"] = sorted( list(objs["snmp_v3"]["views"].values()), key=lambda k, sk="view": k[sk], ) else: objs = {} ansible_facts["ansible_network_resources"].pop("snmp_server", None) params = utils.remove_empties( - snmp_server_parser.validate_config( - self.argument_spec, {"config": objs}, redact=True - ) + snmp_server_parser.validate_config(self.argument_spec, {"config": objs}, redact=True) ) facts["snmp_server"] = params.get("config", {}) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts diff --git a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py index bce2731..019706b 100644 --- a/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py +++ b/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py @@ -1,180 +1,169 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos static_routes fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type -from re import findall, search, M from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) +from re import M, findall, search + +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( Static_routesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_route_type, ) class Static_routesFacts(object): """The vyos static_routes fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Static_routesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for static_routes :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead objs = [] r_v4 = [] r_v6 = [] af = [] - static_routes = findall( - r"set protocols static route(6)? (\S+)", data, M - ) + static_routes = findall(r"set protocols static route(6)? (\S+)", data, M) if static_routes: for route in set(static_routes): route_regex = r" %s .+$" % route[1] cfg = findall(route_regex, data, M) sr = self.render_config(cfg) sr["dest"] = route[1].strip("'") afi = self.get_afi(sr["dest"]) if afi == "ipv4": r_v4.append(sr) else: r_v6.append(sr) if r_v4: afi_v4 = {"afi": "ipv4", "routes": r_v4} af.append(afi_v4) if r_v6: afi_v6 = {"afi": "ipv6", "routes": r_v6} af.append(afi_v6) config = {"address_families": af} if config: objs.append(config) ansible_facts["ansible_network_resources"].pop("static_routes", None) facts = {} if objs: facts["static_routes"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) + params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["static_routes"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ next_hops_conf = "\n".join(filter(lambda x: ("next-hop" in x), conf)) blackhole_conf = "\n".join(filter(lambda x: ("blackhole" in x), conf)) routes_dict = { "blackhole_config": self.parse_blackhole(blackhole_conf), "next_hops": self.parse_next_hop(next_hops_conf), } return routes_dict def parse_blackhole(self, conf): blackhole = None if conf: distance = search(r"^.*blackhole distance (.\S+)", conf, M) bh = conf.find("blackhole") if distance is not None: blackhole = {} value = distance.group(1).strip("'") blackhole["distance"] = int(value) elif bh: blackhole = {} blackhole["type"] = "blackhole" return blackhole def get_afi(self, address): route_type = get_route_type(address) if route_type == "route": return "ipv4" elif route_type == "route6": return "ipv6" def parse_next_hop(self, conf): nh_list = None if conf: nh_list = [] hop_list = findall(r"^.*next-hop (.+)", conf, M) if hop_list: for hop in hop_list: distance = search(r"^.*distance (.\S+)", hop, M) interface = search(r"^.*interface (.\S+)", hop, M) dis = hop.find("disable") hop_info = hop.split(" ") - nh_info = { - "forward_router_address": hop_info[0].strip("'") - } + nh_info = {"forward_router_address": hop_info[0].strip("'")} if interface: nh_info["interface"] = interface.group(1).strip("'") if distance: value = distance.group(1).strip("'") nh_info["admin_distance"] = int(value) elif dis >= 1: nh_info["enabled"] = False for element in nh_list: - if ( - element["forward_router_address"] - == nh_info["forward_router_address"] - ): + if element["forward_router_address"] == nh_info["forward_router_address"]: if "interface" in nh_info.keys(): element["interface"] = nh_info["interface"] if "admin_distance" in nh_info.keys(): - element["admin_distance"] = nh_info[ - "admin_distance" - ] + element["admin_distance"] = nh_info["admin_distance"] if "enabled" in nh_info.keys(): element["enabled"] = nh_info["enabled"] nh_info = None if nh_info is not None: nh_list.append(nh_info) return nh_list diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py index ddff1bb..b1bb27c 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py @@ -1,1421 +1,1373 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Bgp_address_family parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_bgp_af_aggregate_address(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"] if config_data["aggregate_address"].get("as_set"): command += afi + " aggregate-address {prefix} as-set".format( **config_data["aggregate_address"] ) if config_data["aggregate_address"].get("summary_only"): command += afi + " aggregate-address {prefix} summary-only".format( **config_data["aggregate_address"] ) return command def _tmplt_bgp_af_redistribute_metric(config_data): if config_data["address_family"]["redistribute"].get("metric"): afi = config_data["address_family"]["afi"] + "-unicast" - command = "protocols bgp {as_number} address-family ".format( - **config_data - ) + command = "protocols bgp {as_number} address-family ".format(**config_data) if config_data["address_family"]["redistribute"].get("metric"): command += afi + " redistribute {protocol} metric {metric}".format( **config_data["address_family"]["redistribute"] ) return command def _tmplt_bgp_af_redistribute_route_map(config_data): if config_data["address_family"]["redistribute"].get("route_map"): afi = config_data["address_family"]["afi"] + "-unicast" - command = "protocols bgp {as_number} address-family ".format( - **config_data - ) + command = "protocols bgp {as_number} address-family ".format(**config_data) if config_data["address_family"]["redistribute"].get("route_map"): - command += ( - afi - + " redistribute {protocol} route-map {route_map}".format( - **config_data["address_family"]["redistribute"] - ) + command += afi + " redistribute {protocol} route-map {route_map}".format( + **config_data["address_family"]["redistribute"] ) return command def _tmplt_bgp_af_redistribute_table(config_data): if config_data["address_family"]["redistribute"].get("table"): afi = config_data["address_family"]["afi"] + "-unicast" - command = "protocols bgp {as_number} address-family ".format( - **config_data - ) + command = "protocols bgp {as_number} address-family ".format(**config_data) if config_data["address_family"]["redistribute"].get("table"): command += afi + " table {table}".format( **config_data["address_family"]["redistribute"] ) return command def _tmplt_bgp_af_delete_redistribute(config_data): afi = config_data["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} address-family ".format(**config_data) config_data = config_data["address_family"] - command += afi + " redistribute {protocol}".format( - **config_data["redistribute"] - ) + command += afi + " redistribute {protocol}".format(**config_data["redistribute"]) return command def _tmplt_bgp_af_neighbor_distribute_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) - cmd += "{neighbor_address} address-family ".format( - **config_data["neighbors"] - ) + cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["distribute_list"]: command.append( - cmd - + afi - + " distribute-list " - + list_el["action"] - + " " - + str(list_el["acl"]) + cmd + afi + " distribute-list " + list_el["action"] + " " + str(list_el["acl"]) ) return command def _tmplt_bgp_af_neighbor_route_map(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) - cmd += "{neighbor_address} address-family ".format( - **config_data["neighbors"] - ) + cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["route_map"]: command.append( - cmd - + afi - + " route-map " - + list_el["action"] - + " " - + str(list_el["route_map"]) + cmd + afi + " route-map " + list_el["action"] + " " + str(list_el["route_map"]) ) return command def _tmplt_bgp_af_neighbor_prefix_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) - cmd += "{neighbor_address} address-family ".format( - **config_data["neighbors"] - ) + cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["prefix_list"]: command.append( - cmd - + afi - + " prefix-list " - + list_el["action"] - + " " - + str(list_el["prefix_list"]) + cmd + afi + " prefix-list " + list_el["action"] + " " + str(list_el["prefix_list"]) ) return command def _tmplt_bgp_af_neighbor_filter_list(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) - cmd += "{neighbor_address} address-family ".format( - **config_data["neighbors"] - ) + cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for list_el in config_data["filter_list"]: command.append( - cmd - + afi - + " filter-list " - + list_el["action"] - + " " - + str(list_el["path_list"]) + cmd + afi + " filter-list " + list_el["action"] + " " + str(list_el["path_list"]) ) return command def _tmplt_bgp_af_neighbor_attribute(config_data): command = [] afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" cmd = "protocols bgp {as_number} neighbor ".format(**config_data) - cmd += "{neighbor_address} address-family ".format( - **config_data["neighbors"] - ) + cmd += "{neighbor_address} address-family ".format(**config_data["neighbors"]) config_data = config_data["neighbors"]["address_family"] for k in config_data["attribute_unchanged"].keys(): if config_data["attribute_unchanged"][k]: k = re.sub("_", "-", k) c = cmd + afi + " attribute-unchanged " + k command.append(c) return command def _tmplt_bgp_af_neighbor_delete(config_data): afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} ".format(**config_data) command += ( - "neighbor {neighbor_address} address-family ".format( - **config_data["neighbors"] - ) - + afi + "neighbor {neighbor_address} address-family ".format(**config_data["neighbors"]) + afi ) config_data = config_data["neighbors"]["address_family"] if config_data.get("allowas_in"): command += " allowas-in" elif config_data.get("as_override"): command += " as-override" elif config_data.get("attribute_unchanged"): command += " attribute-unchanged" elif config_data.get("capability"): command += " capability" elif config_data.get("default_originate"): command += " default-originate" elif config_data.get("maximum_prefix"): command += " maximum-prefix" elif config_data.get("nexthop_local"): command += " nexthop-local" elif config_data.get("nexthop_self"): command += " nexthop-self" elif config_data.get("peer_group"): command += " peer-group" elif config_data.get("remote_private_as"): command += " remote-private-as" elif config_data.get("route_reflector_client"): command += " route-reflector-client" elif config_data.get("route_server_client"): command += " route-server-client" elif config_data.get("soft_reconfiguration"): command += " soft-reconfiguration" elif config_data.get("unsuppress_map"): command += " unsuppress-map" elif config_data.get("weight"): command += " weight" elif config_data.get("filter_list"): command += " filter-list" elif config_data.get("prefix_list"): command += " prefix-list" elif config_data.get("distribute_list"): command += " distribute-list" elif config_data.get("route_map"): command += " route-map" return command def _tmplt_bgp_af_neighbor(config_data): afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast" command = "protocols bgp {as_number} ".format(**config_data) command += ( - "neighbor {neighbor_address} address-family ".format( - **config_data["neighbors"] - ) - + afi + "neighbor {neighbor_address} address-family ".format(**config_data["neighbors"]) + afi ) config_data = config_data["neighbors"]["address_family"] if config_data.get("allowas_in"): command += " allowas-in number {allowas_in}".format(**config_data) elif config_data.get("as_override"): command += " as-override" elif config_data.get("capability"): command += " capability " if config_data["capability"].get("dynamic"): command += "dynamic" elif config_data["capability"].get("orf"): command += " prefix-list {orf}".format(**config_data["capability"]) elif config_data.get("default_originate"): - command += " default-originate route-map {default_originate}".format( - **config_data - ) + command += " default-originate route-map {default_originate}".format(**config_data) elif config_data.get("maximum_prefix"): command += " maximum-prefix {maximum_prefix}".format(**config_data) elif config_data.get("nexthop_local"): command += " nexthop-local" elif config_data.get("nexthop_self"): command += " nexthop-self" elif config_data.get("peer_group"): command += " peer-group {peer_group}".format(**config_data) elif config_data.get("remote_private_as"): command += " remote-private-as" elif config_data.get("route_reflector_client"): command += " route-reflector-client" elif config_data.get("route_server_client"): command += " route-server-client" elif config_data.get("soft_reconfiguration"): command += " soft-reconfiguration inbound" elif config_data.get("unsuppress_map"): command += " unsuppress-map {unsuppress_map}".format(**config_data) elif config_data.get("weight"): command += " weight {weight}".format(**config_data) return command class Bgp_address_familyTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Bgp_address_familyTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ { "name": "address_family", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast", "compval": "as_number", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", } } } }, { "name": "aggregate_address", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+aggregate-address \s+(?P
\S+) \s*(?Pas-set)* \s*(?Psummary-only)* $""", re.VERBOSE, ), "setval": _tmplt_bgp_af_aggregate_address, "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast aggregate-address" + " {{ address_family.aggregate_address.prefix }}", "compval": "address_family.aggregate_address", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "aggregate_address": [ { "prefix": "{{ address }}", "as_set": "{{ True if as_set is defined }}", "summary_only": "{{ True if summary_only is defined }}" } ] } } } }, { "name": "network.backdoor", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+backdoor *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }} backdoor", "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}", "compval": "address_family.networks.backdoor", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "backdoor": "{{ True }}" } ] } } } }, { "name": "network.path_limit", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+path-limit \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network" + "{{ address_family.networks.prefix }} path-limit {{ address_family.networks.path_limit }}", "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.address }}", "compval": "address_family.networks.path_limit", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "path_limit": "{{ limit|int }}" } ] } } } }, { "name": "network.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+network \s+(?P
\S+) \s+route-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network" + " {{ address_family.networks.prefix }} route-map {{ address_family.networks.route_map }}", "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}", "compval": "address_family.networks.route_map", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "networks": [ { "prefix": "{{ address }}", "route_map": "{{ map }}" } ] } } } }, { "name": "redistribute.metric", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) \s+metric \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute_metric, "remval": _tmplt_bgp_af_delete_redistribute, "compval": "address_family.redistribute.metric", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", "metric": "{{ val|int }}" } ] } } } }, { "name": "redistribute.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) \s+route-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute_route_map, "remval": _tmplt_bgp_af_delete_redistribute, "compval": "address_family.redistribute.route_map", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "protocol": "{{ proto }}", "route_map": "{{ map }}" } ] } } } }, { "name": "redistribute.table", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+address-family \s+(?P\S+)-unicast \s+redistribute \s+table \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute_table, "remval": _tmplt_bgp_af_delete_redistribute, "compval": "address_family.redistribute.table", "result": { "as_number": "{{ as_num }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "redistribute": [ { "table": "{{ tab }}" } ] } } } }, { "name": "neighbors", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family", "compval": "neighbors", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", } } } }, { "name": "neighbors.address_family", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family {{ neighbors.address_family.afi }}-unicast", "compval": "neighbors", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", } } } } } }, { "name": "neighbors.allowas_in", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+allowas-in \s+number \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.allowas_in", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "allowas_in": "{{ num }}" } } } } } }, { "name": "neighbors.as_override", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+as-override *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.as_override", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "as_override": "{{ True }}" } } } } } }, { "name": "neighbors.attribute_unchanged.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pas-path) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.as_path", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "as_path": "{{ True }}" } } } } } } }, { "name": "neighbors.attribute_unchanged.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pmed) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.med", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "med": "{{ True }}" } } } } } } }, { "name": "neighbors.attribute_unchanged.next_hop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+attribute-unchanged \s+(?Pnext-hop) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_attribute, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.attribute_unchanged.next_hop", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "attribute_unchanged": { "next_hop": "{{ True }}" } } } } } } }, { "name": "neighbors.capability_dynamic", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+capability \s+dynamic *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.capability.dynamic", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "capability": { "dynamic": "{{ true }}" } } } } } } }, { "name": "neighbors.capability_orf", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+capability \s+prefix-list \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.capability.orf", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "capability": { "orf": "{{ orf }}" } } } } } } }, { "name": "neighbors.default_originate", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+default-originate \s+route-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.default_originate", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "default_originate": "{{ map }}" } } } } } }, { "name": "neighbors.distribute_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+distribute-list \s+(?Pexport|import) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_distribute_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.distribute_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "distribute_list": [ { "action": "{{ action }}", "acl": "{{ list }}" } ] } } } } } }, { "name": "neighbors.prefix_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+prefix-list \s+(?Pexport|import) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.prefix_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "prefix_list": [ { "action": "{{ action }}", "prefix_list": "{{ list }}" } ] } } } } } }, { "name": "neighbors.filter_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+filter-list \s+(?Pexport|import) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.filter_list", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "filter_list": [ { "action": "{{ action }}", "path_list": "{{ list }}" } ] } } } } } }, { "name": "neighbors.maximum_prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+maximum-prefix \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.maximum_prefix", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "maximum_prefix": "{{ num }}" } } } } } }, { "name": "neighbors.nexthop_local", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+nexthop-local *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.nexthop_local", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "nexthop_local": "{{ True }}" } } } } } }, { "name": "neighbors.nexthop_self", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+nexthop-self *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.nexthop_self", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "nexthop_self": "{{ True }}" } } } } } }, { "name": "neighbors.peer_group", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+peer-group \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.peer_group", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "peer_group": "{{ name }}" } } } } } }, { "name": "neighbors.remove_private_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+remove-private-as *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.remove_private_as", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "remove_private_as": "{{ True }}" } } } } } }, { "name": "neighbors.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-map \s+(?Pexport|import) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_map", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_map": [ { "action": "{{ action }}", "route_map": "{{ map }}" } ] } } } } } }, { "name": "neighbors.route_reflector_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-reflector-client *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_reflector_client", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_reflector_client": "{{ True }}" } } } } } }, { "name": "neighbors.route_server_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+route-server-client *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.route_server_client", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "route_server_client": "{{ True }}" } } } } } }, { "name": "neighbors.soft_reconfiguration", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+soft-reconfiguration \s+inbound *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.soft_reconfiguration", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "soft_reconfiguration": "{{ True }}" } } } } } }, { "name": "neighbors.unsuppress_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+unsuppress-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.unsuppress_map", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "unsuppress_map": "{{ map }}" } } } } } }, { "name": "neighbors.weight", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+address-family \s+(?P\S+)-unicast \s+weight \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, "remval": _tmplt_bgp_af_neighbor_delete, "compval": "neighbors.address_family.weight", "result": { "as_number": "{{ as_num }}", "neighbors": { "{{ address }}": { "neighbor_address": "{{ address }}", "address_family": { "{{ afi }}": { "afi": "{{ afi }}", "weight": "{{ num }}" } } } } } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py index 0671ff7..0f7fd8b 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py @@ -1,2043 +1,2013 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Bgp_global parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re -from ansible.module_utils.six import iteritems +from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_bgp_params_confederation(config_data): command = [] for list_el in config_data["bgp_params"]["confederation"]: for k, v in iteritems(list_el): command.append( - "protocols bgp {as_number} parameters confederation ".format( - **config_data - ) + "protocols bgp {as_number} parameters confederation ".format(**config_data) + k + " " + str(v) ) return command def _tmplt_bgp_maximum_paths(config_data): command = [] for list_el in config_data["maximum_paths"]: command.append( "protocols bgp {as_number} maximum-paths ".format(**config_data) + list_el["path"] + " " + str(list_el["count"]) ) return command def _tmplt_bgp_aggregate_address(config_data): - command = "protocols bgp {as_number} aggregate-address".format( - **config_data - ) + command = "protocols bgp {as_number} aggregate-address".format(**config_data) if config_data["aggregate_address"].get("as_set"): - command += " {prefix} as-set".format( - **config_data["aggregate_address"] - ) + command += " {prefix} as-set".format(**config_data["aggregate_address"]) if config_data["aggregate_address"].get("summary_only"): - command += " {prefix} summary-only".format( - **config_data["aggregate_address"] - ) + command += " {prefix} summary-only".format(**config_data["aggregate_address"]) return command def _tmplt_delete_bgp_maximum_paths(config_data): command = "protocols bgp {as_number} maximum-paths".format(**config_data) return command def _tmplt_bgp_params_default(config_data): - command = "protocols bgp {as_number} parameters default".format( - **config_data - ) + command = "protocols bgp {as_number} parameters default".format(**config_data) if config_data["bgp_params"]["default"].get("no_ipv4_unicast"): command += " no-ipv4-unicast" if config_data["bgp_params"]["default"].get("local_pref"): - command += " local-pref {local_pref}".format( - **config_data["bgp_params"]["default"] - ) + command += " local-pref {local_pref}".format(**config_data["bgp_params"]["default"]) return command def _tmplt_bgp_delete_redistribute(config_data): command = ( "protocols bgp {as_number} redistribute ".format(**config_data) + config_data["redistribute"]["protocol"] ) return command def _tmplt_bgp_neighbor_timers(config_data): command = [] for k, v in iteritems(config_data["neighbor"]["timers"]): command.append( "protocols bgp {as_number} neighbor ".format(**config_data) + config_data["neighbor"]["address"] + " timers " + k + " " + str(v) ) return command def _tmplt_bgp_timers(config_data): command = [] for k, v in iteritems(config_data["timers"]): command.append( - "protocols bgp {as_number} ".format(**config_data) - + "timers " - + k - + " " - + str(v) + "protocols bgp {as_number} ".format(**config_data) + "timers " + k + " " + str(v) ) return command def _tmplt_bgp_neighbor_attribute_unchanged_as_path(config_data): command = "protocols bgp {as_number} ".format( **config_data - ) + "neighbor {address} attribute-unchanged as-path".format( - **config_data["neighbor"] - ) + ) + "neighbor {address} attribute-unchanged as-path".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_attribute_unchanged_med(config_data): command = "protocols bgp {as_number} ".format( **config_data - ) + "neighbor {address} attribute-unchanged med".format( - **config_data["neighbor"] - ) + ) + "neighbor {address} attribute-unchanged med".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_attribute_unchanged_next_hop(config_data): command = "protocols bgp {as_number} ".format( **config_data - ) + "neighbor {address} attribute-unchanged next-hop".format( - **config_data["neighbor"] - ) + ) + "neighbor {address} attribute-unchanged next-hop".format(**config_data["neighbor"]) return command def _tmplt_bgp_neighbor_distribute_list(config_data): command = [] for list_el in config_data["neighbor"]["distribute_list"]: command.append( "protocols bgp {as_number} ".format(**config_data) - + "neighbor {address} distribute-list ".format( - **config_data["neighbor"] - ) + + "neighbor {address} distribute-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["acl"]) ) return command def _tmplt_bgp_neighbor_route_map(config_data): command = [] for list_el in config_data["neighbor"]["route_map"]: command.append( "protocols bgp {as_number} ".format(**config_data) + "neighbor {address} route-map ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["route_map"]) ) return command def _tmplt_bgp_neighbor_prefix_list(config_data): command = [] for list_el in config_data["neighbor"]["prefix_list"]: command.append( "protocols bgp {as_number} ".format(**config_data) - + "neighbor {address} prefix-list ".format( - **config_data["neighbor"] - ) + + "neighbor {address} prefix-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["prefix_list"]) ) return command def _tmplt_bgp_neighbor_filter_list(config_data): command = [] for list_el in config_data["neighbor"]["filter_list"]: command.append( "protocols bgp {as_number} ".format(**config_data) - + "neighbor {address} filter-list ".format( - **config_data["neighbor"] - ) + + "neighbor {address} filter-list ".format(**config_data["neighbor"]) + list_el["action"] + " " + str(list_el["path_list"]) ) return command def _tmplt_bgp_params_distance(config_data): command = ( - "protocols bgp {as_number} parameters distance global ".format( - **config_data - ) + "protocols bgp {as_number} parameters distance global ".format(**config_data) + config_data["bgp_params"]["distance"]["type"] + " " + str(config_data["bgp_params"]["distance"]["value"]) ) return command class Bgp_globalTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Bgp_globalTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ { "name": "router", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }}", "compval": "as_number", "result": { "as_number": "{{ as_num }}", } }, { "name": "aggregate_address", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+aggregate-address \s+(?P
\S+) \s*(?Pas-set)* \s*(?Psummary-only)* $""", re.VERBOSE, ), "setval": _tmplt_bgp_aggregate_address, "remval": "protocols bgp {{ as_number }} aggregate-address {{ aggregate_address.prefix }}", "compval": "aggregate_address", "result": { "as_number": "{{ as_num }}", "aggregate_address": [ { "prefix": "{{ address }}", "as_set": "{{ True if as_set is defined }}", "summary_only": "{{ True if summary_only is defined }}" } ] } }, { "name": "maximum_paths", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+maximum-paths \s+(?Pebgp|ibgp) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_maximum_paths, "remval": _tmplt_delete_bgp_maximum_paths, "compval": "maximum_paths", "result": { "as_number": "{{ as_num }}", "maximum_paths": [ { "path": "{{ path }}", "count": "{{ count }}", } ] } }, { "name": "neighbor.advertisement_interval", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+advertisement-interval \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", "remval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval", "compval": "neighbor.advertisement_interval", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "advertisement_interval": "{{ interval }}" } } } }, { "name": "neighbor.allowas_in", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+allowas-in \s+number \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} allowas-in number {{ neighbor.allowas_in }}", "compval": "neighbor.allowas_in", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "allowas_in": "{{ count }}" } } } }, { "name": "neighbor.as_override", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+as-override *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} as-override", "compval": "neighbor.as_override", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "as_override": "{{ True }}" } } } }, { "name": "neighbor.attribute_unchanged.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pas-path) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_as_path, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'as_path' }}": "{{ True }}" } } } } }, { "name": "neighbor.attribute_unchanged.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pmed) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_med, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'med' }}": "{{ True }}" } } } } }, { "name": "neighbor.attribute_unchanged.next_hop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+attribute-unchanged \s+(?Pnext-hop) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_attribute_unchanged_next_hop, "compval": "neighbor.attribute_unchanged", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "attribute_unchanged": { "{{ 'next_hop' }}": "{{ True }}" } } } } }, { "name": "neighbor.capability_dynamic", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+capability \s+(?Pdynamic) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability dynamic", "compval": "neighbor.capability.dynamic", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "capability": { "dynamic": "{{ True if dynamic is defined}}" } } } } }, { "name": "neighbor.capability_orf", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+capability \s+orf \s+prefix-list \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", "compval": "neighbor.capability.orf", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "capability": { "orf": "{{ orf }}" } } } } }, { "name": "neighbor.default_originate", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+default-originate \s+route-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", "compval": "neighbor.advertisement_interval", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "default_originate": "{{ map }}" } } } }, { "name": "neighbor.description", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+description \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} description {{ neighbor.description }}", "compval": "neighbor.description", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "description": "{{ desc }}" } } } }, { "name": "neighbor.disable_capability_negotiation", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+disable-capability-negotiation *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-capability-negotiation", "compval": "neighbor.disable_capability_negotiation", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_capability_negotiation": "{{ True }}" } } } }, { "name": "neighbor.disable_connected_check", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+disable-connected-check *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-connected-check", "compval": "neighbor.disable_connected_check", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_connected_check": "{{ True }}" } } } }, { "name": "neighbor.disable_send_community", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+disable-send-community \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", "compval": "neighbor.disable_send_community", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "disable_send_community": "{{ comm }}" } } } }, { "name": "neighbor.distribute_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+distribute-list \s+(?Pexport|import) \s+(?P\d+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_distribute_list, "compval": "neighbor.distribute_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "distribute_list": [ { "action": "{{ action }}", "acl": "{{ list }}" } ] } } } }, { "name": "neighbor.ebgp_multihop", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+ebgp-multihop \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", "compval": "neighbor.ebgp_multihop", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "ebgp_multihop": "{{ hop|int }}" } } } }, { "name": "neighbor.filter_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+filter-list \s+(?Pexport|import) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, "compval": "neighbor.filter_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "filter_list": [ { "action": "{{ action }}", "path_list": "{{ list }}" } ] } } } }, { "name": "neighbor.local_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+local-as \s+(?P\S+) \s+no-prepend *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} local-as {{ neighbor.local_as }} no-prepend", "compval": "neighbor.local_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "local_as": "{{ as }}" } } } }, { "name": "neighbor.maximum_prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+maximum-prefix \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", "compval": "neighbor.maximum_prefix", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "maximum_prefix": "{{ num }}" } } } }, { "name": "neighbor.nexthop_self", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+nexthop-self *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} nexthop-self", "compval": "neighbor.nexthop_self", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "nexthop_self": "{{ True }}" } } } }, { "name": "neighbor.override_capability", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+override-capability *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} override-capability", "compval": "neighbor.override_capability", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "override_capability": "{{ True }}" } } } }, { "name": "neighbor.passive", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+passive *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} nexthop-self", "compval": "neighbor.passive", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "passive": "{{ True }}" } } } }, { "name": "neighbor.password", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+password \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} password {{ neighbor.address }}", "compval": "neighbor.password", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "password": "{{ pwd }}" } } } }, { "name": "neighbor.peer_group_name", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+peer-group \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", "compval": "neighbor.peer_group_name", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "peer_group_name": "{{ name }}" } } } }, { "name": "neighbor.port", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+port \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} port {{ neighbor.port }}", "compval": "neighbor.port", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "port": "{{ num|int }}" } } } }, { "name": "neighbor.prefix_list", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+prefix-list \s+(?Pexport|import) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, "compval": "neighbor.prefix_list", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "prefix_list": [ { "action": "{{ action }}", "prefix_list": "{{ list }}" } ] } } } }, { "name": "neighbor.remote_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+remote-as \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", "compval": "neighbor.remote_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "remote_as": "{{ num|int }}" } } } }, { "name": "neighbor.remove_private_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+remote-private-as *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-private-as", "compval": "neighbor.remove_private_as", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "remove_private_as": "{{ True }}" } } } }, { "name": "neighbor.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+route-map \s+(?Pexport|import) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, "compval": "neighbor.route_map", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_map": [ { "action": "{{ action }}", "route_map": "{{ map }}" } ] } } } }, { "name": "neighbor.route_reflector_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+route-reflector-client *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} router-reflector-client", "compval": "neighbor.route_reflector_client", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_reflector_client": "{{ True }}" } } } }, { "name": "neighbor.route_server_client", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+route-server-client *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} route-server-client", "compval": "neighbor.route_server_client", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "route_server_client": "{{ True }}" } } } }, { "name": "neighbor.shutdown", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+shutdown *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} shutdown", "compval": "neighbor.shutdown", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "shutdown": "{{ True }}" } } } }, { "name": "neighbor.soft_reconfiguration", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+soft-reconfiguration \s+inbound *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} soft-reconfiguration", "compval": "neighbor.soft_reconfiguration", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "soft_reconfiguration": "{{ True }}" } } } }, { "name": "neighbor.strict_capability_match", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+strict-capability-match *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} strict-capability-match", "compval": "neighbor.strict_capability_match", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "strict_capability_match": "{{ True }}" } } } }, { "name": "neighbor.unsuppress_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+unsuppress-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", "compval": "neighbor.unsuppress_map", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "unsuppress_map": "{{ map }}" } } } }, { "name": "neighbor.update_source", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+update-source \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", "compval": "neighbor.update_source", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "update_source": "{{ src }}" } } } }, { "name": "neighbor.weight", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+weight \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", "compval": "neighbor.weight", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "weight": "{{ num }}" } } } }, { "name": "neighbor.ttl_security", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+ttl-security \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", "compval": "neighbor.ttl_security", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "ttl_security": "{{ ttl|int }}" } } } }, { "name": "neighbor.timers", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+neighbor \s+(?P
\S+) \s+timers \s+(?Pconnect|holdtime|keepalive) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, "remval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} timers", "compval": "neighbor.timers", "result": { "as_number": "{{ as_num }}", "neighbor": { "{{ address }}": { "address": "{{ address }}", "timers": { "{{ type }}": "{{ sec }}" } } } } }, { "name": "network.backdoor", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+network \s+(?P
\S+) \s+backdoor *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} network {{ network.address }} backdoor", "remval": "protocols bgp {{ as_number }} network {{ network.address }}", "compval": "network.backdoor", "result": { "as_number": "{{ as_num }}", "network": [ { "address": "{{ address }}", "backdoor": "{{ True }}" } ] } }, { "name": "network.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+network \s+(?P
\S+) \s+route-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} network {{ network.address }} route-map {{ network.route_map }}", "remval": "protocols bgp {{ as_number }} network {{ network.address }}", "compval": "network.route_map", "result": { "as_number": "{{ as_num }}", "network": [ { "address": "{{ address }}", "route_map": "{{ map }}" } ] } }, { "name": "redistribute.metric", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+redistribute \s+(?P\S+) \s+metric \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} redistribute {{ redistribute.protocol }} metric {{ redistribute.metric }}", "remval": _tmplt_bgp_delete_redistribute, "compval": "redistribute", "result": { "as_number": "{{ as_num }}", "redistribute": [ { "protocol": "{{ proto }}", "metric": "{{ val|int }}" } ] } }, { "name": "redistribute.route_map", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+redistribute \s+(?P\S+) \s+route-map \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} redistribute {{ redistribute.protocol }} route-map {{ redistribute.route_map }}", "remval": _tmplt_bgp_delete_redistribute, "compval": "redistribute", "result": { "as_number": "{{ as_num }}", "redistribute": [ { "protocol": "{{ proto }}", "route_map": "{{ val }}" } ] } }, { "name": "timers", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+timers \s+(?P\S+) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, "remval": "protocols bgp {{ as_number }} timers", "compval": "timers", "result": { "as_number": "{{ as_num }}", "timers": { "{{ type }}": "{{ val }}", } } }, { "name": "bgp_params.always_compare_med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+always-compare-med *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters always-compare-med", "compval": "bgp_params.always_compare_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "always_compare_med": "{{ True }}", } } }, { "name": "bgp_params.bestpath.as_path", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+bestpath \s+as-path \s+(?Pconfed|ignore) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters bestpath as-path {{ bgp_params.bestpath.as_path }}", "compval": "bgp_params.bestpath.as_path", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "as_path": "{{ path }}", } } } }, { "name": "bgp_params.bestpath.compare_routerid", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+bestpath \s+compare-routerid *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters bestpath compare-routerid", "compval": "bgp_params.bestpath.compare_routerid", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "compare_routerid": "{{ True }}", } } } }, { "name": "bgp_params.bestpath.med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+bestpath \s+med \s+(?Pconfed|missing-as-worst) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters bestpath med {{ bestpath.med }}", "compval": "bgp_params.bestpath.med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "bestpath": { "med": "{{ path }}", } } } }, { "name": "bgp_params.cluster_id", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+cluster-id \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters cluster-id {{ bgp_params.cluster_id }}", "compval": "bgp_params.cluster_id", "result": { "as_number": "{{ as_num }}", "bgp_params": { "cluster_id": "{{ id }}", } } }, { "name": "bgp_params.confederation", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+confederation \s+(?Pidentifier|peers) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, "compval": "bgp_params.always_compare_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "confederation": [ { "peers": "{{ val if type == 'peers' }}", "identifier": "{{ val if type == 'identifier' }}" } ] } } }, { "name": "bgp_params.dampening_half_life", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+half-life \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening half-life {{ bgp_params.dampening.half_life}}", "compval": "bgp_params.dampening.half_life", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "half_life": "{{ val }}" } } } }, { "name": "bgp_params.dampening_max_suppress_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+max-suppress-time \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", "compval": "bgp_params.dampening.max_suppress_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "max_suppress_time": "{{ val }}" } } } }, { "name": "bgp_params.dampening_re_use", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+re-use \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening re-use {{ bgp_params.dampening.re_use}}", "compval": "bgp_params.dampening.re_use", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "re_use": "{{ val }}" } } } }, { "name": "bgp_params.dampening_start_suppress_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+dampening \s+start-suppress-time \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", "compval": "bgp_params.dampening.start_suppress_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "dampening": { "start_suppress_time": "{{ val }}" } } } }, { "name": "bgp_params.default", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+default \s*(?Pno-ipv4-unicast)* \s*(?Plocal-pref\s\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, "remval": "protocols bgp {{ as_number }} parameters default", "compval": "bgp_params.default", "result": { "as_number": "{{ as_num }}", "bgp_params": { "default": { "no_ipv4_unicast": "{{ True if no_ipv4_unicast is defined }}", "local_pref": "{{ local_pref.split(" " )[1] if local_pref is defined }}" } } } }, { "name": "bgp_params.deterministic_med", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+deterministic-med *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters deterministic-med", "compval": "bgp_params.deterministic_med", "result": { "as_number": "{{ as_num }}", "bgp_params": { "deterministic_med": "{{ True }}", } } }, { "name": "bgp_params.disbale_network_import_check", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+disable-network-import-check *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters disable-network-import-check", "compval": "bgp_params.disable_network_import_check", "result": { "as_number": "{{ as_num }}", "bgp_params": { "disable_network_import_check": "{{ True }}", } } }, { "name": "bgp_params.distance.prefix", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+distance\sprefix \s+(?P\S+) \s+distance \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", "compval": "bgp_params.distance.prefix", "remval": "protocols bgp {{ as_number }} parameters distance prefix", "result": { "as_number": "{{ as_num }}", "bgp_params": { "distance": [ { "prefix": "{{ prefix }}", "value": "{{ val }}" } ] } } }, { "name": "bgp_params.distance.global", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+distance\sglobal \s+(?P\S+) \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, "remval": "protocols bgp {{ as_number }} parameters distance global", "compval": "bgp_params.distance", "result": { "as_number": "{{ as_num }}", "bgp_params": { "distance": [ { "type": "{{ type }}", "value": "{{ val }}" } ] } } }, { "name": "bgp_params.enforce_first_as", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+enforce-first-as *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters enforce-first-as", "compval": "bgp_params.enforce_first_as", "result": { "as_number": "{{ as_num }}", "bgp_params": { "enforce_first_as": "{{ True }}", } } }, { "name": "bgp_params.graceful_restart", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+graceful-restart\s+stalepath-time \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", "compval": "bgp_params.graceful_restart", "result": { "as_number": "{{ as_num }}", "bgp_params": { "graceful_restart": "{{ val }}", } } }, { "name": "bgp_params.log_neighbor_changes", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+log-neighbor-changes *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters log-neighbor-changes", "compval": "bgp_params.log_neighbor_changes", "result": { "as_number": "{{ as_num }}", "bgp_params": { "log_neighbor_changes": "{{ True }}", } } }, { "name": "bgp_params.no_client_to_client_reflection", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+no-client-to-client-reflection *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters no-client-to-client-reflection", "compval": "bgp_params.log_neighbor_changes", "result": { "as_number": "{{ as_num }}", "bgp_params": { "no_client_to_client_reflection": "{{ True }}", } } }, { "name": "bgp_params.no_fast_external_failover", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+no-fast-external-failover *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters no-fast-external-failover", "compval": "bgp_params.no_fast_external_failover", "result": { "as_number": "{{ as_num }}", "bgp_params": { "no_fast_external_failover": "{{ True }}", } } }, { "name": "bgp_params.routerid", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+router-id \s+(?P\S+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters router-id {{ bgp_params.router_id }}", "compval": "bgp_params.router_id", "result": { "as_number": "{{ as_num }}", "bgp_params": { "router_id": "{{ id }}", } } }, { "name": "bgp_params.scan_time", "getval": re.compile( r""" ^set \s+protocols \s+bgp \s+(?P\d+) \s+parameters \s+scan-time \s+(?P\d+) *$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters scan-time {{ bgp_params.scan_time }}", "compval": "bgp_params.scan_time", "result": { "as_number": "{{ as_num }}", "bgp_params": { "scan_time": "{{ val }}", } } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/hostname.py b/plugins/module_utils/network/vyos/rm_templates/hostname.py index 79caee6..7352794 100644 --- a/plugins/module_utils/network/vyos/rm_templates/hostname.py +++ b/plugins/module_utils/network/vyos/rm_templates/hostname.py @@ -1,47 +1,48 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Hostname parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class HostnameTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(HostnameTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ # service snmp community <> { "name": "hostname", "getval": re.compile( r""" ^set\ssystem\shost-name \s+(?P\S+) $""", re.VERBOSE), "setval": "system host-name {{ hostname }}", "result": { "hostname": "{{ name }}" } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/logging_global.py b/plugins/module_utils/network/vyos/rm_templates/logging_global.py index 8cfe7a6..82a4963 100644 --- a/plugins/module_utils/network/vyos/rm_templates/logging_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/logging_global.py @@ -1,388 +1,381 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Logging_global parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def tmplt_params(config_data): def templt_common(val, tmplt): if val.get("facility"): tmplt += " facility {facility}".format(facility=val["facility"]) if val.get("severity"): tmplt += " level {level}".format(level=val["severity"]) if val.get("protocol"): tmplt += " protocol {protocol}".format(protocol=val["protocol"]) return tmplt tmplt = "" if config_data.get("global_params"): val = config_data.get("global_params") if not val.get("archive"): tmplt += "system syslog global" tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("console"): val = config_data.get("console") tmplt += "system syslog console" tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("users"): val = config_data.get("users") if val.get("username") and not val.get("archive"): - tmplt += "system syslog user {username}".format( - username=val["username"] - ) + tmplt += "system syslog user {username}".format(username=val["username"]) if val.get("facilities"): tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("hosts"): val = config_data.get("hosts") - if ( - val.get("hostname") - and not val.get("archive") - and not val.get("port") - ): - tmplt += "system syslog host {hostname}".format( - hostname=val["hostname"] - ) + if val.get("hostname") and not val.get("archive") and not val.get("port"): + tmplt += "system syslog host {hostname}".format(hostname=val["hostname"]) if val.get("facilities"): tmplt = templt_common(val.get("facilities"), tmplt) elif config_data.get("files"): val = config_data.get("files") if val.get("path") and not val.get("archive"): tmplt += "system syslog file {path}".format(path=val["path"]) if val.get("facilities"): tmplt = templt_common(val.get("facilities"), tmplt) return tmplt class Logging_globalTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Logging_globalTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ { "name": "syslog.state", "getval": re.compile( r""" ^set\ssystem (\s(?Psyslog)) $""", re.VERBOSE), "setval": "system syslog", "result": { "syslog": { "state": "{{ 'enabled' if syslog is defined else 'disabled' }}", } } }, { "name": "console.facilities", "getval": re.compile( r""" ^set\ssystem\ssyslog\sconsole\sfacility (\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE), "setval": tmplt_params, "remval": "system syslog console facility {{ console.facilities.facility }}", "result": { "console": { "facilities": [{ "facility": "{{ facility }}", "severity": "{{ level }}", }, ] } } }, { "name": "console.state", "getval": re.compile( r""" ^set\ssystem\ssyslog (\s(?Pconsole)) $""", re.VERBOSE), "setval": "system syslog console", "result": { "console": { "state": "{{ 'enabled' if console is defined else 'disabled' }}", } } }, { "name": "files.archive.state", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\s(?Parchive)) $""", re.VERBOSE), "setval": "system syslog file {{ files.path }} archive", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "archive": { "state": "{{ 'enabled' if archive is defined else 'disabled' }}", }, }, } } }, { "name": "files.archive.size", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\sarchive\ssize\s(?P'(\d+)'))? $""", re.VERBOSE), "setval": "system syslog file {{ files.path }} archive size {{ files.archive.size }}", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "archive": { "size": "{{ size }}", }, }, } } }, { "name": "files.archive.file_num", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\sarchive\sfile\s(?P'(\d+)'))? $""", re.VERBOSE), "setval": "system syslog file {{ files.path }} archive file {{ files.archive.file_num }}", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "archive": { "file_num": "{{ file_num }}", }, }, } } }, { "name": "files", "getval": re.compile( r""" ^set\ssystem\ssyslog\sfile (\s(?P\S+))? (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE), "setval": tmplt_params, "remval": "system syslog file{{ (' ' + files.path) if files.path|d('') is defined else '' }}", "result": { "files": { "{{ path }}": { "path": "{{ path }}", "facilities": [{ "facility": "{{ facility }}", "severity": "{{ level }}", }, ] }, } } }, { "name": "global_params.state", "getval": re.compile( r""" ^set\ssystem\ssyslog (\s(?Pglobal)) $""", re.VERBOSE), "setval": "system syslog global", "result": { "global_params": { "state": "{{ 'enabled' if global is defined else 'disabled' }}", } } }, { "name": "global_params.archive.state", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal (\s(?Parchive)) $""", re.VERBOSE), "setval": "system syslog global archive", "result": { "global_params": { "archive": { "state": "{{ 'enabled' if archive is defined else 'disabled' }}", }, } } }, { "name": "global_params.archive.file_num", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\sarchive\sfile (\s(?P'(\d+)'))? $""", re.VERBOSE), "setval": "system syslog global archive file {{ global_params.archive.file_num }}", "result": { "global_params": { "archive": { "file_num": "{{ file_num }}", }, } } }, { "name": "global_params.archive.size", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\sarchive\ssize (\s(?P'(\d+)'))? $""", re.VERBOSE), "setval": "system syslog global archive size {{ global_params.archive.size }}", "result": { "global_params": { "archive": { "size": "{{ size }}", }, } } }, { "name": "global_params.marker_interval", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\smarker\sinterval (\s(?P'(\d+)'))? $""", re.VERBOSE), "setval": "system syslog global marker interval {{ global_params.marker_interval }}", "remval": "system syslog global marker", "result": { "global_params": { "marker_interval": "{{ marker_interval }}", } } }, { "name": "global_params.preserve_fqdn", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal (\s(?Ppreserve-fqdn)) $""", re.VERBOSE), "setval": "system syslog global preserve-fqdn", "result": { "global_params": { "preserve_fqdn": "{{ True if preserve_fqdn is defined }}", } } }, { "name": "global_params.facilities", "getval": re.compile( r""" ^set\ssystem\ssyslog\sglobal\sfacility (\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE), "setval": tmplt_params, "remval": "system syslog global facility {{ global_params.facilities.facility }}", "result": { "global_params": { "facilities": [{ "facility": "{{ facility }}", "severity": "{{ level }}", }, ], } } }, { "name": "hosts.port", "getval": re.compile( r""" ^set\ssystem\ssyslog\shost (\s(?P\S+)) (\sport\s(?P'(\d+)')) $""", re.VERBOSE), "setval": "system syslog host {{ hosts.hostname }} port {{ hosts.port }}", "result": { "hosts": { "{{ hostname }}": { "hostname": "{{ hostname }}", "port": "{{ port }}", }, } } }, { "name": "hosts", "getval": re.compile( r""" ^set\ssystem\ssyslog\shost (\s(?P\S+)) (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7])) (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? (\sprotocol\s(?P'(udp|tcp)'))? $""", re.VERBOSE), "setval": tmplt_params, "remval": "system syslog host {{ hosts.hostname }}", "result": { "hosts": { "{{ hostname }}": { "hostname": "{{ hostname }}", "facilities": [{ "facility": "{{ facility }}", "severity": "{{ level }}", "protocol": "{{ protocol }}", }, ] }, } } }, { "name": "users", "getval": re.compile( r""" ^set\ssystem\ssyslog\suser (\s(?P\S+))? (\sfacility\s(?Pall|auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local[0-7]))? (\slevel\s(?P'(emerg|alert|crit|err|warning|notice|info|debug|all)'))? $""", re.VERBOSE), "setval": tmplt_params, "remval": "system syslog user {{ users.username }}", "result": { "users": { "{{ username }}": { "username": "{{ username }}", "facilities": [{ "facility": "{{ facility }}", "severity": "{{ level }}", }, ] } } } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/ntp_global.py b/plugins/module_utils/network/vyos/rm_templates/ntp_global.py index f4589a2..5254035 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ntp_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/ntp_global.py @@ -1,129 +1,128 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Ntp parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class NtpTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} - super(NtpTemplate, self).__init__( - lines=lines, tmplt=self, prefix=prefix, module=module - ) + super(NtpTemplate, self).__init__(lines=lines, tmplt=self, prefix=prefix, module=module) # fmt: off PARSERS = [ # set system ntp allow_clients address
{ "name": "allow_clients", "getval": re.compile( r""" ^set\ssystem\sntp\sallow-clients\saddress (\s(?P\S+))? $""", re.VERBOSE), "setval": "system ntp allow-clients address {{allow_clients}}", "result": { "allow_clients": ["{{ipaddress}}"] } }, # set system ntp allow_clients { "name": "allow_clients_delete", "getval": re.compile( r""" ^set\ssystem\sntp\sallow-clients $""", re.VERBOSE), "setval": "system ntp allow-clients", "result": { } }, # set system ntp listen_address
{ "name": "listen_addresses", "getval": re.compile( r""" ^set\ssystem\sntp\slisten-address (\s(?P\S+))? $""", re.VERBOSE), "setval": "system ntp listen-address {{listen_addresses}}", "result": { "listen_addresses": ["{{ip_address}}"] } }, # set system ntp listen_address { "name": "listen_addresses_delete", "getval": re.compile( r""" ^set\ssystem\sntp\slisten-address $""", re.VERBOSE), "setval": "system ntp listen-address", "result": { } }, # set system ntp server { "name": "server", "getval": re.compile( r""" ^set\ssystem\sntp\sserver (\s(?P\S+))? $""", re.VERBOSE), "setval": "system ntp server {{server}}", "result": { "servers": { "{{name}}": { "server": "{{name}}" } } } }, # set system ntp server { "name": "options", "getval": re.compile( r""" ^set\ssystem\sntp\sserver \s(?P\S+) \s(?Pnoselect|dynamic|pool|preempt|prefer)? $""", re.VERBOSE), "setval": "system ntp server {{server}} {{options}}", "result": { "servers": { "{{name}}": { "server": "{{name}}", "options": ["{{options}}"] } } } } ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py index bda17df..de3beba 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py @@ -1,743 +1,729 @@ # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Ospf_interfaces parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( get_interface_type, ) def _get_parameters(data): if data["afi"] == "ipv6": val = ["ospfv3", "ipv6"] else: val = ["ospf", "ip"] return val def _tmplt_ospf_int_delete(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( - "interfaces " - + int_type - + " {name} ".format(**config_data) - + params[1] - + " " - + params[0] + "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] ) return command def _tmplt_ospf_int_cost(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " cost {cost}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_auth_password(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " authentication plaintext-password {plaintext_password}".format( **config_data["address_family"]["authentication"] ) ) return command def _tmplt_ospf_int_auth_md5(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " authentication md5 key-id {key_id} ".format( **config_data["address_family"]["authentication"]["md5_key"] ) - + "md5-key {key}".format( - **config_data["address_family"]["authentication"]["md5_key"] - ) + + "md5-key {key}".format(**config_data["address_family"]["authentication"]["md5_key"]) ) return command def _tmplt_ospf_int_auth_md5_delete(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " authentication" ) return command def _tmplt_ospf_int_bw(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " bandwidth {bandwidth}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_hello_interval(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] - + " hello-interval {hello_interval}".format( - **config_data["address_family"] - ) + + " hello-interval {hello_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_dead_interval(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] - + " dead-interval {dead_interval}".format( - **config_data["address_family"] - ) + + " dead-interval {dead_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_mtu_ignore(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " mtu-ignore" ) return command def _tmplt_ospf_int_network(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " network {network}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_priority(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " priority {priority}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_retransmit_interval(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] - + " retransmit-interval {retransmit_interval}".format( - **config_data["address_family"] - ) + + " retransmit-interval {retransmit_interval}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_transmit_delay(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] - + " transmit-delay {transmit_delay}".format( - **config_data["address_family"] - ) + + " transmit-delay {transmit_delay}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_ifmtu(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " ifmtu {ifmtu}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_instance(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " instance-id {instance}".format(**config_data["address_family"]) ) return command def _tmplt_ospf_int_passive(config_data): int_type = get_interface_type(config_data["name"]) params = _get_parameters(config_data["address_family"]) command = ( "interfaces " + int_type + " {name} ".format(**config_data) + params[1] + " " + params[0] + " passive" ) return command class Ospf_interfacesTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Ospf_interfacesTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ { "name": "ip_ospf", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) *$""", re.VERBOSE, ), "remval": _tmplt_ospf_int_delete, "compval": "address_family", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', } } } }, { "name": "authentication_password", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+authentication \s+plaintext-password \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, "compval": "address_family.authentication", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "authentication": { "plaintext_password": "{{ text }}" } } } } }, { "name": "authentication_md5", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+authentication \s+md5 \s+key-id \s+(?P\d+) \s+md5-key \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, "remval": _tmplt_ospf_int_auth_md5_delete, "compval": "address_family.authentication", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "authentication": { "md5_key": { "key_id": "{{ id }}", "key": "{{ text }}" } } } } } }, { "name": "bandwidth", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+bandwidth \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_bw, "compval": "address_family.bandwidth", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "bandwidth": "{{ bw }}" } } } }, { "name": "cost", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+cost \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_cost, "compval": "address_family.cost", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "cost": "{{ val }}" } } } }, { "name": "hello_interval", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+hello-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_hello_interval, "compval": "address_family.hello_interval", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "hello_interval": "{{ val }}" } } } }, { "name": "dead_interval", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+dead-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_dead_interval, "compval": "address_family.dead_interval", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "dead_interval": "{{ val }}" } } } }, { "name": "mtu_ignore", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+(?P\'mtu-ignore\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_mtu_ignore, "compval": "address_family.mtu_ignore", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "mtu_ignore": "{{ True if mtu is defined }}" } } } }, { "name": "network", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+network \s+(?P\S+) *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_network, "compval": "address_family.network", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "network": "{{ val }}" } } } }, { "name": "priority", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+priority \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_priority, "compval": "address_family.priority", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "priority": "{{ val }}" } } } }, { "name": "retransmit_interval", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+retransmit-interval \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_retransmit_interval, "compval": "address_family.retransmit_interval", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "retransmit_interval": "{{ val }}" } } } }, { "name": "transmit_delay", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+transmit-delay \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_transmit_delay, "compval": "address_family.transmit_delay", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "transmit_delay": "{{ val }}" } } } }, { "name": "ifmtu", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+ifmtu \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_ifmtu, "compval": "address_family.ifmtu", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "ifmtu": "{{ val }}" } } } }, { "name": "instance", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+instance-id \s+(?P\'\d+\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_instance, "compval": "address_family.instance", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "instance": "{{ val }}" } } } }, { "name": "passive", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+(?P\'passive\') *$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_passive, "compval": "address_family.passive", "result": { "name": "{{ name }}", "address_family": { "{{ afi }}": { "afi": '{{ "ipv4" if afi == "ip" else "ipv6" }}', "passive": "{{ True if pass is defined }}" } } } }, { "name": "interface_name", "getval": re.compile( r""" ^set \s+interfaces \s+(?P\S+) \s+(?P\S+) .*$""", re.VERBOSE, ), "setval": "set interface {{ type }} {{ name }}", "result": { "name": "{{ name }}", } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py b/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py index 9a66a8d..00a157e 100644 --- a/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py +++ b/plugins/module_utils/network/vyos/rm_templates/prefix_lists.py @@ -1,265 +1,266 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Prefix_lists parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class Prefix_listsTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Prefix_listsTemplate, self).__init__( lines=lines, tmplt=self, module=module, prefix=prefix ) # fmt: off PARSERS = [ # policy prefix-list { "name": "name", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }}", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", } } } }, }, # policy prefix-list description { "name": "description", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \sdescription\s'(?P.+)' $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} description '{{ description }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "description": "{{ description }}" } } } }, }, # policy prefix-list rule { "name": "sequence", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }}", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}" } } } } } }, }, # policy prefix-list rule action { "name": "action", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \saction\s'(?Ppermit|deny)' $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} action '{{ action }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "action": "{{ action }}" } } } } } }, }, # policy prefix-list rule description { "name": "rule_description", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sdescription\s'(?P.+)' $""", re.VERBOSE), "compval": "description", "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} description '{{ description }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "description": "{{ rule_description }}" } } } } } }, }, # policy prefix-list rule ge { "name": "ge", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sge\s'(?P\d+)' $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} ge '{{ ge }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "ge": "{{ ge }}" } } } } } }, }, # policy prefix-list rule le { "name": "le", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sle\s'(?P\d+)' $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} le '{{ le }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "le": "{{ le }}" } } } } } }, }, # policy prefix-list rule prefix { "name": "prefix", "getval": re.compile( r""" ^set \spolicy \sprefix-(?P\S+) \s(?P\S+) \srule\s(?P\d+) \sprefix\s'(?P\S+)' $""", re.VERBOSE), "setval": "policy prefix-{{ 'list' if afi == 'ipv4' else 'list6' }} {{ name }} rule {{ sequence }} prefix '{{ prefix }}'", "result": { "{{ 'ipv4' if afi == 'list' else 'ipv6' }}": { "afi": "{{ 'ipv4' if afi == 'list' else 'ipv6' }}", "prefix_lists": { "{{ name }}": { "name": "{{ name }}", "entries": { "{{ sequence }}": { "sequence": "{{ sequence }}", "prefix": "{{ prefix }}" } } } } } }, }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps.py b/plugins/module_utils/network/vyos/rm_templates/route_maps.py index 262721e..1652a23 100644 --- a/plugins/module_utils/network/vyos/rm_templates/route_maps.py +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py @@ -1,1293 +1,1292 @@ # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Route_maps parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class Route_mapsTemplate(NetworkTemplate): def __init__(self, lines=None): prefix = {"set": "set", "remove": "delete"} - super(Route_mapsTemplate, self).__init__( - lines=lines, tmplt=self, prefix=prefix - ) + super(Route_mapsTemplate, self).__init__(lines=lines, tmplt=self, prefix=prefix) # fmt: off PARSERS = [ { "name": "route_map", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+) *$""", re.VERBOSE, ), "compval": "route_map", "setval": "policy route-map {{route_map}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', } } }, }, { "name": "sequence", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+) *$""", re.VERBOSE, ), "compval": "sequence", "setval": "policy route-map {{route_map}} rule {{sequence}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}" } } } } } }, { "name": "call", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+) *$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "call": "{{call}}" } } } } } }, { "name": "description", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+) *$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "description": "{{description}}" } } } } } }, { "name": "action", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+) *$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "action": "{{action}}" } } } } } }, { "name": "continue_sequence", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+) *$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "continue_sequence": "{{continue}}" } } } } } }, { "name": "on_match_next", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\s(?Pnext) *$""", re.VERBOSE, ), "compval": "on_match.next", "setval": "policy route-map {{route_map}} rule {{sequence}} on-match next", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "on_match": { "next": "{{True if next is defined}}" } } } } } } }, { "name": "on_match_goto", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+) *$""", re.VERBOSE, ), "compval": "on_match.goto", "setval": "policy route-map {{route_map}} rule {{sequence}} on-match goto {{on_match.goto}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "on_match": { "goto": "{{goto}}" } } } } } } }, { "name": "set_aggregator_ip", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.aggregator.ip", "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator ip {{set.aggregator.ip}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "aggregator": { "ip": "{{ip}}" } } } } } } } }, { "name": "set_aggregator_as", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.aggregator.as", "setval": "policy route-map {{route_map}} rule {{sequence}} set aggregator as {{set.aggregator.as}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "aggregator": { "as": "{{as}}" } } } } } } } }, { "name": "set_as_path_exclude", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path-exclude\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.as_path_exclude", "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-exclude {{set.as_path_exclude}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "as_path_exclude": "{{as}}" } } } } } } }, { "name": "set_as_path_prepend", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path-prepend\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.as_path_prepend", "setval": "policy route-map {{route_map}} rule {{sequence}} set as-path-prepend {{set.as_path_prepend}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "as_path_prepend": "{{as}}" } } } } } } }, { "name": "set_atomic_aggregate", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\satomic-aggregate(?P) *$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} set atomic-aggregate", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "atomic_aggregate": "{{True if as is defined}}" } } } } } } }, { "name": "set_bgp_extcommunity_rt", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.bgp_extcommunity_rt", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set bgp-extcommunity-rt {{set.bgp_extcommunity_rt}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "bgp_extcommunity_rt": "{{bgp}}" } } } } } } }, { "name": "set_comm_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\scomm-list\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set comm-list comm-list {{set.comm_list.comm_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "comm_list": {"comm_list": "{{comm_list}}"} } } } } } } }, { "name": "set_comm_list_delete", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\sdelete(?P\S+) *$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set comm-list delete", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "comm_list": {"delete": "{{True if delete is defined}}"} } } } } } } }, { "name": "set_extcommunity_rt", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-rt\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.extcommunity_rt", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity-rt {{set.extcommunity_rt}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_rt": "{{extcommunity_rt}}" } } } } } } }, { "name": "set_extcommunity_soo", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-soo\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.extcommunity_soo", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set extcommunity-soo {{set.extcommunity_soo}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "extcommunity_soo": "{{set.extcommunity_soo}}" } } } } } } }, { "name": "set_ip_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.ip_next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set ip-next-hop {{set.ip_next_hop}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "ip_next_hop": "{{ip_next_hop}}" } } } } } } }, { "name": "set_ipv6_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sipv6-next-hop \s(?Pglobal|local) \s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.ipv6_next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set ipv6-next-hop {{set.ipv6_next_hop.ip_type}} {{set.ipv6_next_hop.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "ipv6_next_hop": { "ip_type": "{{type}}", "value": "{{value}}" } } } } } } } }, { "name": "set_large_community", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slarge-community\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.large_community", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set large-community {{set.large_community}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "large_community": "{{large_community}}" } } } } } } }, { "name": "set_local_preference", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.local_preference", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set local-preference {{set.local_preference}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "local_preference": "{{local_preference}}" } } } } } } }, { "name": "set_metric", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.metric", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set metric {{set.metric}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "metric": "{{metric}}" } } } } } } }, { "name": "set_metric_type", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.metric_type", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set metric-type {{set.metric_type}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "metric_type": "{{metric_type}}" } } } } } } }, { "name": "set_origin", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.origin", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set origin {{set.origin}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "origin": "{{origin}}" } } } } } } }, { "name": "set_originator_id", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.originator_id", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set originator-id {{set.originator_id}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "originator_id": "{{originator_id}}" } } } } } } }, { "name": "set_src", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.src", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set src {{set.src}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "src": "{{src}}" } } } } } } }, { "name": "set_tag", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.tag", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set tag {{set.tag}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "tag": "{{tag}}" } } } } } } }, { "name": "set_weight", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.weight", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set weight {{set.weight}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "weight": "{{weight}}" } } } } } } }, { "name": "set_community", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scommunity\s(?P\S+) *$""", re.VERBOSE, ), "compval": "set.community.value", "setval": "policy route-map {{route_map}} rule {{sequence}} " "set community {{set.community.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "set": { "community": { "value": "{{value}}", } } } } } } } }, { "name": "match_as_path", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.as_path", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match as-path {{match.as_path}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "as_path": "{{as_path}}" } } } } } } }, { "name": "match_community_community_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.community.community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community community-list {{match.community.community_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"community_list": "{{community_list}}"} } } } } } } }, { "name": "match_community_exact_match", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\sexact-match(?P) *$""", re.VERBOSE, ), "compval": "match.community.exact_match", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match community exact-match", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "community": {"exact_match": "{{True if exact_match is defined}}"} } } } } } } }, { "name": "match_extcommunity", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.extcommunity", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match extcommunity {{match.extcommunity}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "extcommunity": "{{extcommunity}}" } } } } } } }, { "name": "match_interface", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.interface", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match interface {{match.interface}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "interface": "{{interface}}" } } } } } } }, { "name": "match_large_community_large_community_list", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.large_community_large_community_list", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match large-community large-community-list {{match.large_community_large_community_list}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "large_community_large_community_list": "{{lc}}" } } } } } } }, { "name": "match_metric", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.metric", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match metric {{match.metric}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "metric": "{{metric}}" } } } } } } }, { "name": "match_origin", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.origin", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match origin {{match.origin}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "origin": "{{origin}}" } } } } } } }, { "name": "match_peer", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.peer", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match peer {{match.peer}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "peer": "{{peer}}" } } } } } } }, { "name": "match_ip_address", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\saddress \s(?Paccess-list|prefix-list) \s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.ip.address", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip address {{match.ip.address.list_type}} {{match.ip.address.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "address": { "list_type": "{{list_type}}", "value": "{{value}}" } } } } } } } } }, { "name": "match_ip_next_hop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\snexthop \s(?Paccess-list|prefix-list) \s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.ip.next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip nexthop {{match.ip.next_hop.list_type}} {{match.ip.next_hop.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "next_hop": { "list_type": "{{list_type}}", "value": "{{value}}" } } } } } } } } }, { "name": "match_ip_route_source", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\sroute-source \s(?Paccess-list|prefix-list) \s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.ip.route_source", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ip route-source {{match.ip.route_source.list_type}} {{match.ip.route_source.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ip": { "route_source": { "list_type": "{{list_type}}", "value": "{{value}}" } } } } } } } } }, { "name": "match_ipv6_address", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\saddress \s(?Paccess-list|prefix-list) \s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.ipv6.address", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ipv6 address {{match.ipv6.address.list_type}} {{match.ipv6.address.value}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ipv6": { "address": { "list_type": "{{list_type}}", "value": "{{value}}" } } } } } } } } }, { "name": "match_ipv6_nexthop", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\snexthop \s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.ipv6.next_hop", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match ipv6 nexthop {{match.ipv6.next_hop}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "ipv6": { "next_hop": "{{value}}" } } } } } } } }, { "name": "match_rpki", "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\srpki \s(?P\S+) *$""", re.VERBOSE, ), "compval": "match.rpki", "setval": "policy route-map {{route_map}} rule {{sequence}} " "match rpki {{match.rpki}}", "result": { "route_maps": { "{{ route_map }}": { "route_map": '{{ route_map }}', "entries": { "{{sequence}}": { "sequence": "{{sequence}}", "match": { "rpki": "{{value}}" } } } } } } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py index c1e385a..15a0804 100644 --- a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py +++ b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py @@ -1,594 +1,585 @@ # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The Snmp_server parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re + from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) def _tmplt_snmp_server_communities(config_data): config_data = config_data["communities"] command = [] cmd = "service snmp community {name}".format(**config_data) if "authorization_type" in config_data: - auth_cmd = cmd + " authorization {authorization_type}".format( - **config_data - ) + auth_cmd = cmd + " authorization {authorization_type}".format(**config_data) command.append(auth_cmd) if "clients" in config_data: for c in config_data["clients"]: client_cmd = cmd + " client " + c command.append(client_cmd) if "networks" in config_data: for n in config_data["networks"]: network_command = cmd + " network " + n command.append(network_command) if not command: command.append(cmd) return command def _tmplt_snmp_server_trap_target(config_data): config_data = config_data["trap_target"] command = "service snmp trap-target {address}".format(**config_data) if "authorization_type" in config_data: command += " authorization {authorization_type}".format(**config_data) if "client" in config_data: command += " client {client}".format(**config_data) if "network" in config_data: command += " network {network}".format(**config_data) return command def _tmplt_snmp_server_v3_groups(config_data): config_data = config_data["snmp_v3"]["groups"] command = [] cmd = "service snmp v3 group {group}".format(**config_data) if "mode" in config_data: mode_cmd = cmd + " mode {mode}".format(**config_data) command.append(mode_cmd) if "seclevel" in config_data: sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) command.append(sec_cmd) if "view" in config_data: view_cmd = cmd + " view {view}".format(**config_data) command.append(view_cmd) return command def _tmplt_snmp_server_v3_trap_target(config_data): config_data = config_data["snmp_v3"]["trap_targets"] command = "service snmp v3 trap-target {address} ".format(**config_data) if "authentication" in config_data: command += " auth" config_data = config_data["authentication"] if "privacy" in config_data: command += " privacy" config_data = config_data["privacy"] if "type" in config_data: command += " type {mode}".format(**config_data) if "encrypted_key" in config_data: command += " encrypted-key {encrypted_key}".format(**config_data) if "plaintext_key" in config_data: command += " plaintext-key {plaintext_key}".format(**config_data) return command def _tmplt_snmp_server_v3_user(config_data): config_data = config_data["snmp_v3"]["users"] command = [] cmd = "service snmp v3 user {user}".format(**config_data) for k in ["authentication", "privacy"]: if config_data.get(k): config = config_data[k] if k == "authentication": val = " auth" else: val = " privacy" if "type" in config: type_cmd = cmd + val + " type {type}".format(**config) command.append(type_cmd) if "encrypted_key" in config: - enc_cmd = ( - cmd - + val - + " encrypted-key {encrypted_key}".format(**config) - ) + enc_cmd = cmd + val + " encrypted-key {encrypted_key}".format(**config) command.append(enc_cmd) if "plaintext_key" in config: - plain_cmd = ( - cmd - + val - + " plaintext-key {plaintext_key}".format(**config) - ) + plain_cmd = cmd + val + " plaintext-key {plaintext_key}".format(**config) command.append(plain_cmd) return command def _tmplt_snmp_server_v3_views(config_data): config_data = config_data["snmp_v3"]["views"] command = "service snmp v3 view {view} oid {oid}".format(**config_data) if "exclude" in config_data: command += " exclude {exclude}".format(**config_data) if "mask" in config_data: command += " mask {mask}".format(**config_data) return command class Snmp_serverTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(Snmp_serverTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module ) # fmt: off PARSERS = [ # service snmp community <> { "name": "communities", "getval": re.compile( r""" ^set\sservice\ssnmp\scommunity \s+(?P\S+) \s*(?Pauthorization\srw|authorization\sro)* \s*(client\s(?P\S+))* \s*(network\s(?P\S+))* $""", re.VERBOSE), "setval": _tmplt_snmp_server_communities, "result": { "communities": { "{{ name }}": { "name": "{{ name }}", "clients": ['{{ client if client is defined else "None" }}'], "networks": ['{{ network if network is defined else "None" }}'], "authorization_type": '{{ auth.split(" ")[1] if auth is defined else None }}' } } } }, # service snmp contact <> { "name": "contact", "getval": re.compile( r""" ^set\sservice\ssnmp\scontact \s+(?P\S+) *$""", re.VERBOSE), "setval": "service snmp contact {{ contact }}", "result": { "contact": "{{ name }}" } }, # service snmp description <> { "name": "description", "getval": re.compile( r""" ^set\sservice\ssnmp\sdescription \s+(?P\S+) *$""", re.VERBOSE), "setval": "service snmp description {{ description }}", "result": { "description": "{{ name }}" } }, # service snmp listen-address <> port <> { "name": "listen_addresses", "getval": re.compile( r""" ^set\sservice\ssnmp\slisten-address \s+(?P\S+) \s*(port)* \s*(?P\d+)* $""", re.VERBOSE), "setval": "service snmp listen-address {{ listen_addresses.address }}" "{{ (' port ' + listen_addresses.port|string) if listen_addresses.port is defined else '' }}", "result": { "listen_addresses": { "{{ addr }}": { "address": "{{ addr }}", "port": "{{ port }}" } } } }, # service snmp location <> { "name": "location", "getval": re.compile( r""" ^set\sservice\ssnmp\slocation \s(?P.*) $""", re.VERBOSE), "setval": "service snmp location {{ '\\'' + location + '\\''}}", "result": { "location": "{{ name }}" } }, # service snmp smux-peer <> { "name": "smux_peer", "getval": re.compile( r""" ^set\sservice\ssnmp\ssmux-peer \s+(?P\S+) *$""", re.VERBOSE), "setval": "service snmp smux-peer {{ smux_peer }}", "result": { "smux_peer": "{{ name }}" } }, # service snmp trap-source <> { "name": "trap_source", "getval": re.compile( r""" ^set\sservice\ssnmp\strap-source \s+(?P\S+) *$""", re.VERBOSE), "setval": "service snmp trap-source {{ trap_source }}", "result": { "trap_source": "{{ name }}" } }, # service snmp trap-target <> { "name": "trap_target", "getval": re.compile( r""" ^set\sservice\ssnmp\strap-target \s+(?P\S+) \s*(?Pcommunity\s\S+)* \s*(?Pport\s\d+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_trap_target, "result": { "trap_target": { "address": "{{ name }}", "community": "{{ comm.split(" ")[1] if comm is defined else None }}", "port": "{{ port.split(" ")[1] if port is defined else None }}", } } }, # service snmp v3 engineid <> { "name": "snmp_v3.engine_id", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sengineid \s+(?P\S+) *$""", re.VERBOSE), "setval": "service snmp v3 engineid {{ snmp_v3.engine_id }}", "result": { "snmp_v3": { "engine_id": "{{ name }}", } } }, # service snmp v3 group <> { "name": "snmp_v3.groups", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sgroup \s+(?P\S+) \s*(?Pmode\s\S+)* \s*(?Pseclevel\s\S+)* \s*(?Pview\s\S+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_v3_groups, "result": { "snmp_v3": { "groups": { "{{ name }}": { "group": "{{ name }}", "mode": '{{ mode.split(" ")[1] if mode is defined else None }}', "seclevel": '{{ sec.split(" ")[1] if sec is defined else None }}', "view": '{{ view.split(" ")[1] if view is defined else None }}', } } } } }, # service snmp v3 trap-target <> auth <> { "name": "snmp_v3.trap_targets.authentication", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+auth \s*(?Pencrypted-key\s\S+)* \s*(?Pplaintext-key\s\S+)* \s*(?Ptype\s\S+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_v3_trap_target, "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "authentication": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', } } } } } }, # service snmp v3 trap-target <> port <> { "name": "snmp_v3.trap_targets.port", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Pport\s\d+)* $""", re.VERBOSE), "setval": "service snmp v3 trap-target port {{ snmp_v3.trap_targets.port }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "port": "{{ port }}" } } } } }, # service snmp v3 trap-target <> protocol <> { "name": "snmp_v3.trap_targets.protocol", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Pprotocol\s\S+)* $""", re.VERBOSE), "setval": "service snmp v3 trap-target protocol {{ snmp_v3.trap_targets.protocol }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "protocol": "{{ protocol }}" } } } } }, # service snmp v3 trap-target <> type <> { "name": "snmp_v3.trap_targets.type", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Ptype\s\S+)* $""", re.VERBOSE), "setval": "service snmp v3 trap-target type {{ snmp_v3.trap_targets.type }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "type": "{{ type }}" } } } } }, # service snmp v3 trap-target <> user <> { "name": "snmp_v3.trap_targets.user", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+(?Puser\s\S+)* $""", re.VERBOSE), "setval": "service snmp v3 trap-target user {{ snmp_v3.trap_targets.user }}", "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "user": "{{ user }}" } } } } }, # service snmp v3 trap-target <> privacy <> { "name": "snmp_v3.trap_targets.privacy", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+privacy \s*(?Pencrypted-key\s\S+)* \s*(?Pplaintext-key\s\S+)* \s*(?Ptype\s\S+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_v3_trap_target, "result": { "snmp_v3": { "trap_targets": { "{{ name }}": { "address": "{{ name }}", "privacy": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', } } } } } }, # service snmp v3 user <> auth <> { "name": "snmp_v3.users.authentication", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+auth \s*(?Pencrypted-key\s\S+)* \s*(?Pplaintext-key\s\S+)* \s*(?Ptype\s\S+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_v3_user, "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "authentication": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', } } } } } }, # service snmp v3 user <> privacy <> { "name": "snmp_v3.users.privacy", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+privacy \s*(?Pencrypted-key\s\S+)* \s*(?Pplaintext-key\s\S+)* \s*(?Ptype\s\S+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_v3_user, "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "privacy": { "encrypted_key": '{{ enc.split(" ")[1] if enc is defined else None }}', "plaintext_key": '{{ plain.split(" ")[1] if plain is defined else None }}', "type": '{{ type.split(" ")[1] if type is defined else None }}', } } } } } }, # service snmp v3 user <> group <> { "name": "snmp_v3.users.group", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+(?Pgroup\s.+)* $""", re.VERBOSE), "setval": "service snmp v3 user {{ snmp_v3.users.user }} group {{ snmp_v3.users.group }}", "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "group": "{{ group.split(" ")[1] if group is defined else None }}" } } } } }, # service snmp v3 user <> mode <> { "name": "snmp_v3.users.mode", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+(?Pmode\s\S+)* $""", re.VERBOSE), "setval": "service snmp v3 user {{ snmp_v3.users.user }} mode {{ snmp_v3.users.mode }}", "result": { "snmp_v3": { "users": { "{{ name }}": { "user": "{{ name }}", "mode": "{{ mode }}" } } } } }, # service snmp v3 view <> { "name": "snmp_v3.views", "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sview \s+(?P\S+) \s+(?Poid\s\S+) \s*(?Pexclude\s\S+)* \s*(?Pmask\s\S+)* $""", re.VERBOSE), "setval": _tmplt_snmp_server_v3_views, "result": { "snmp_v3": { "views": { "{{ name }}": { "view": "{{ name }}", "oid": '{{ oid.split(" ")[1] if oid is defined else None }}', "exclude": '{{ ex.split(" ")[1] if ex is defined else None }}', "mask": '{{ mask.split(" ")[1] if mask is defined else None }}', } } } } }, ] # fmt: on diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 43f3fc9..4d44744 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -1,280 +1,258 @@ # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # utils from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible.module_utils.six import iteritems from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import iteritems try: import ipaddress HAS_IPADDRESS = True except ImportError: HAS_IPADDRESS = False def search_obj_in_list(name, lst, key="name"): if lst: for item in lst: if item[key] == name: return item return None def get_interface_type(interface): """Gets the type of interface""" if interface.startswith("eth"): return "ethernet" elif interface.startswith("bond"): return "bonding" elif interface.startswith("vti"): return "vti" elif interface.startswith("lo"): return "loopback" elif interface.startswith("vtun"): return "openvpn" elif interface.startswith("wg"): return "wireguard" def dict_delete(base, comparable): """ This function generates a dict containing key, value pairs for keys that are present in the `base` dict but not present in the `comparable` dict. :param base: dict object to base the diff on :param comparable: dict object to compare against base :returns: new dict object with key, value pairs that needs to be deleted. """ to_delete = dict() for key in base: if isinstance(base[key], dict): sub_diff = dict_delete(base[key], comparable.get(key, {})) if sub_diff: to_delete[key] = sub_diff else: if key not in comparable: to_delete[key] = base[key] return to_delete def diff_list_of_dicts(want, have): diff = [] set_w = set(tuple(d.items()) for d in want) set_h = set(tuple(d.items()) for d in have) difference = set_w.difference(set_h) for element in difference: diff.append(dict((x, y) for x, y in element)) return diff def get_lst_diff_for_dicts(want, have, lst): """ This function generates a list containing values that are only in want and not in list in have dict :param want: dict object to want :param have: dict object to have :param lst: list the diff on :return: new list object with values which are only in want. """ if not have: diff = want.get(lst) or [] else: want_elements = want.get(lst) or {} have_elements = have.get(lst) or {} diff = list_diff_want_only(want_elements, have_elements) return diff def get_lst_same_for_dicts(want, have, lst): """ This function generates a list containing values that are common for list in want and list in have dict :param want: dict object to want :param have: dict object to have :param lst: list the comparison on :return: new list object with values which are common in want and have. """ diff = None if want and have: want_list = want.get(lst) or {} have_list = have.get(lst) or {} - diff = [ - i - for i in want_list and have_list - if i in have_list and i in want_list - ] + diff = [i for i in want_list and have_list if i in have_list and i in want_list] return diff def list_diff_have_only(want_list, have_list): """ This function generated the list containing values that are only in have list. :param want_list: :param have_list: :return: new list with values which are only in have list """ if have_list and not want_list: diff = have_list elif not have_list: diff = None else: - diff = [ - i - for i in have_list + want_list - if i in have_list and i not in want_list - ] + diff = [i for i in have_list + want_list if i in have_list and i not in want_list] return diff def list_diff_want_only(want_list, have_list): """ This function generated the list containing values that are only in want list. :param want_list: :param have_list: :return: new list with values which are only in want list """ if have_list and not want_list: diff = None elif not have_list: diff = want_list else: - diff = [ - i - for i in have_list + want_list - if i in want_list and i not in have_list - ] + diff = [i for i in have_list + want_list if i in want_list and i not in have_list] return diff def search_dict_tv_in_list(d_val1, d_val2, lst, key1, key2): """ This function return the dict object if it exist in list. :param d_val1: :param d_val2: :param lst: :param key1: :param key2: :return: """ obj = next( - ( - item - for item in lst - if item[key1] == d_val1 and item[key2] == d_val2 - ), + (item for item in lst if item[key1] == d_val1 and item[key2] == d_val2), None, ) if obj: return obj else: return None def key_value_in_dict(have_key, have_value, want_dict): """ This function checks whether the key and values exist in dict :param have_key: :param have_value: :param want_dict: :return: """ for key, value in iteritems(want_dict): if key == have_key and value == have_value: return True return False def is_dict_element_present(dict, key): """ This function checks whether the key is present in dict. :param dict: :param key: :return: """ for item in dict: if item == key: return True return False def get_ip_address_version(address): """ This function returns the version of IP address :param address: IP address :return: """ if not HAS_IPADDRESS: raise Exception(missing_required_lib("ipaddress")) try: address = unicode(address) except NameError: address = str(address) version = ipaddress.ip_address(address.split("/")[0]).version return version def get_route_type(address): """ This function returns the route type based on IP address :param address: :return: """ version = get_ip_address_version(address) if version == 6: return "route6" elif version == 4: return "route" def _bool_to_str(val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ - return ( - "enable" - if str(val) == "True" - else "disable" - if str(val) == "False" - else val - ) + return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val def _is_w_same(w, h, key): """ This function checks whether the key value is same in desired and target config dictionary. :param w: base config. :param h: target config. :param key:attribute name. :return: True/False. """ return True if h and key in h and h[key] == w[key] else False def _in_target(h, key): """ This function checks whether the target exist and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h else False diff --git a/plugins/module_utils/network/vyos/vyos.py b/plugins/module_utils/network/vyos/vyos.py index 42fc57c..da364f9 100644 --- a/plugins/module_utils/network/vyos/vyos.py +++ b/plugins/module_utils/network/vyos/vyos.py @@ -1,104 +1,100 @@ # This code is part of Ansible, but is an independent component. # This particular file snippet, and this file snippet only, is BSD licensed. # Modules you write using this snippet, which is embedded dynamically by Ansible # still belong to the author of the module, and may assign their own license # to the complete work. # # (c) 2016 Red Hat Inc. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # from __future__ import absolute_import, division, print_function __metaclass__ = type import json from ansible.module_utils._text import to_text from ansible.module_utils.connection import Connection, ConnectionError _DEVICE_CONFIGS = {} def get_connection(module): if hasattr(module, "_vyos_connection"): return module._vyos_connection capabilities = get_capabilities(module) network_api = capabilities.get("network_api") if network_api == "cliconf": module._vyos_connection = Connection(module._socket_path) else: module.fail_json(msg="Invalid connection type %s" % network_api) return module._vyos_connection def get_capabilities(module): if hasattr(module, "_vyos_capabilities"): return module._vyos_capabilities try: capabilities = Connection(module._socket_path).get_capabilities() except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) module._vyos_capabilities = json.loads(capabilities) return module._vyos_capabilities def get_config(module, flags=None, format=None): flags = [] if flags is None else flags global _DEVICE_CONFIGS if _DEVICE_CONFIGS != {}: return _DEVICE_CONFIGS else: connection = get_connection(module) try: out = connection.get_config(flags=flags, format=format) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) cfg = to_text(out, errors="surrogate_then_replace").strip() _DEVICE_CONFIGS = cfg return cfg def run_commands(module, commands, check_rc=True): connection = get_connection(module) try: - response = connection.run_commands( - commands=commands, check_rc=check_rc - ) + response = connection.run_commands(commands=commands, check_rc=check_rc) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) return response def load_config(module, commands, commit=False, comment=None): connection = get_connection(module) try: - response = connection.edit_config( - candidate=commands, commit=commit, comment=comment - ) + response = connection.edit_config(candidate=commands, commit=commit, comment=comment) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) return response.get("diff") diff --git a/plugins/modules/vyos_banner.py b/plugins/modules/vyos_banner.py index a5f3fb9..0ee10f5 100644 --- a/plugins/modules/vyos_banner.py +++ b/plugins/modules/vyos_banner.py @@ -1,193 +1,186 @@ #!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function __metaclass__ = type # (c) 2017, Ansible by Red Hat, inc # # This file is part of Ansible by Red Hat # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # DOCUMENTATION = """ module: vyos_banner author: Trishna Guha (@trishnaguha) short_description: Manage multiline banners on VyOS devices description: - This will configure both pre-login and post-login banners on remote devices running VyOS. It allows playbooks to add or remote banner text from the active running configuration. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: banner: description: - Specifies which banner that should be configured on the remote device. required: true choices: - pre-login - post-login type: str text: description: - The banner text that should be present in the remote device running configuration. This argument accepts a multiline string, with no empty lines. Requires I(state=present). type: str state: description: - Specifies whether or not the configuration is present in the current devices active running configuration. default: present type: str choices: - present - absent extends_documentation_fragment: - vyos.vyos.vyos """ EXAMPLES = """ - name: configure the pre-login banner vyos.vyos.vyos_banner: banner: pre-login text: | this is my pre-login banner that contains a multiline string state: present - name: remove the post-login banner vyos.vyos.vyos_banner: banner: post-login state: absent """ RETURN = """ commands: description: The list of configuration mode commands to send to the device returned: always type: list sample: - banner pre-login - this is my pre-login banner - that contains a multiline - string """ import re from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_config, load_config, ) def spec_to_commands(updates, module): commands = list() want, have = updates state = module.params["state"] if state == "absent": if have.get("state") != "absent" or ( - have.get("state") != "absent" - and "text" in have.keys() - and have["text"] + have.get("state") != "absent" and "text" in have.keys() and have["text"] ): - commands.append( - "delete system login banner %s" % module.params["banner"] - ) + commands.append("delete system login banner %s" % module.params["banner"]) elif state == "present": - if want["text"] and want["text"].encode().decode( - "unicode_escape" - ) != have.get("text"): - banner_cmd = ( - "set system login banner %s " % module.params["banner"] - ) + if want["text"] and want["text"].encode().decode("unicode_escape") != have.get("text"): + banner_cmd = "set system login banner %s " % module.params["banner"] banner_cmd += want["text"].strip() commands.append(banner_cmd) return commands def config_to_dict(module): data = get_config(module) output = None obj = {"banner": module.params["banner"], "state": "absent"} for line in data.split("\n"): if line.startswith("set system login banner %s" % obj["banner"]): match = re.findall(r"%s (.*)" % obj["banner"], line, re.M) output = match if output: obj["text"] = output[0].encode().decode("unicode_escape") obj["state"] = "present" return obj def map_params_to_obj(module): text = module.params["text"] if text: text = "%r" % (str(text).strip()) return { "banner": module.params["banner"], "text": text, "state": module.params["state"], } def main(): """main entry point for module execution""" argument_spec = dict( banner=dict(required=True, choices=["pre-login", "post-login"]), text=dict(), state=dict(default="present", choices=["present", "absent"]), ) required_if = [("state", "present", ("text",))] module = AnsibleModule( argument_spec=argument_spec, required_if=required_if, supports_check_mode=True, ) warnings = list() result = {"changed": False} if warnings: result["warnings"] = warnings want = map_params_to_obj(module) have = config_to_dict(module) commands = spec_to_commands((want, have), module) result["commands"] = commands if commands: commit = not module.check_mode load_config(module, commands, commit=commit) result["changed"] = True module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_bgp_address_family.py b/plugins/modules/vyos_bgp_address_family.py index ab98e3b..ba56309 100644 --- a/plugins/modules/vyos_bgp_address_family.py +++ b/plugins/modules/vyos_bgp_address_family.py @@ -1,1192 +1,1193 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_bgp_address_family """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_bgp_address_family version_added: 2.1.0 short_description: BGP Address Family Resource Module. description: - This module manages BGP address family configuration of interfaces on devices running VYOS. author: Gomathi Selvi Srinivasan (@GomathiselviS) options: config: description: A dict of BGP global configuration for interfaces. type: dict suboptions: as_number: description: - AS number. type: int address_family: description: BGP address-family parameters. type: list elements: dict suboptions: afi: description: BGP address family settings. type: str choices: ['ipv4', 'ipv6'] aggregate_address: description: - BGP aggregate network. type: list elements: dict suboptions: prefix: description: BGP aggregate network. type: str as_set: description: Generate AS-set path information for this aggregate address. type: bool summary_only: description: Announce the aggregate summary network only. type: bool networks: description: BGP network type: list elements: dict suboptions: prefix: description: BGP network address type: str path_limit: description: AS path hop count limit type: int backdoor: description: Network as a backdoor route. type: bool route_map: description: Route-map to modify route attributes type: str redistribute: description: Redistribute routes from other protocols into BGP type: list elements: dict suboptions: protocol: description: types of routes to be redistributed. type: str choices: ['connected', 'kernel', 'ospf', 'ospfv3', 'rip', 'ripng', 'static'] table: description: Redistribute non-main Kernel Routing Table. type: str route_map: description: Route map to filter redistributed routes type: str metric: description: Metric for redistributed routes. type: int neighbors: description: BGP neighbor type: list elements: dict suboptions: neighbor_address: description: BGP neighbor address (v4/v6). type: str address_family: description: address family. type: list elements: dict suboptions: afi: description: BGP neighbor parameters. type: str choices: ['ipv4', 'ipv6'] allowas_in: description: Number of occurrences of AS number. type: int as_override: description: AS for routes sent to this neighbor to be the local AS. type: bool attribute_unchanged: description: BGP attributes are sent unchanged. type: dict suboptions: as_path: description: as_path attribute type: bool med: description: med attribute type: bool next_hop: description: next_hop attribute type: bool capability: description: Advertise capabilities to this neighbor. type: dict suboptions: dynamic: description: Advertise dynamic capability to this neighbor. type: bool orf: description: Advertise ORF capability to this neighbor. type: str choices: ['send', 'receive'] default_originate: description: Send default route to this neighbor type: str distribute_list: description: Access-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: Access-list to filter outgoing/incoming route updates to this neighbor type: str choices: ['export', 'import'] acl: description: Access-list number. type: int filter_list: description: As-path-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] path_list: description: As-path-list to filter type: str maximum_prefix: description: Maximum number of prefixes to accept from this neighbor nexthop-self Nexthop for routes sent to this neighbor to be the local router. type: int nexthop_local: description: Nexthop attributes. type: bool nexthop_self: description: Nexthop for routes sent to this neighbor to be the local router. type: bool peer_group: description: IPv4 peer group for this peer type: str prefix_list: description: Prefix-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] prefix_list: description: Prefix-list to filter type: str remove_private_as: description: Remove private AS numbers from AS path in outbound route updates type: bool route_map: description: Route-map to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] route_map: description: route-map to filter type: str route_reflector_client: description: Neighbor as a route reflector client type: bool route_server_client: description: Neighbor is route server client type: bool soft_reconfiguration: description: Soft reconfiguration for neighbor type: bool unsupress_map: description: Route-map to selectively unsuppress suppressed routes type: str weight: description: Default weight for routes from this neighbor type: int running_config: type: str description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration command | match bgp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - gathered - parsed - rendered - purged - overridden default: merged """ EXAMPLES = """ # Using merged # Before state # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # vyos@vyos:~$ - name: Merge provided configuration with device configuration vyos.vyos.vyos_bgp_address_family: config: as_number: "100" address_family: - afi: "ipv4" redistribute: - protocol: "static" metric: 50 neighbors: - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv4" allowas_in: 4 as_override: True attribute_unchanged: med: True - afi: "ipv6" default_originate: "map01" distribute_list: - action: "export" acl: 10 - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" maximum_prefix: 45 nexthop_self: True route_map: - action: "export" route_map: "map01" - action: "import" route_map: "map01" weight: 50 # After State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50' # vyos@vyos:~$ # # Module Execution: # # "after": { # "address_family": [ # { # "afi": "ipv4", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "export", # "route_map": "map01" # }, # { # "action": "import", # "route_map": "map01" # } # ], # "weight": 50 # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "before": {}, # "changed": true, # "commands": [ # "set protocols bgp 100 address-family ipv4-unicast redistribute static metric 50", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight 50" # ], # # Using replaced: # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50' # vyos@vyos:~$ - name: Replace provided configuration with device configuration vyos.vyos.vyos_bgp_address_family: config: as_number: "100" neighbors: - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" allowas_in: 4 as_override: True attribute_unchanged: med: True - afi: "ipv6" default_originate: "map01" distribute_list: - action: "export" acl: 10 - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv6" maximum_prefix: 45 nexthop_self: True state: replaced # After State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10' # vyos@vyos:~$ # # # # Module Execution: # "after": { # "address_family": [ # { # "afi": "ipv4", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4" # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "before": { # "address_family": [ # { # "afi": "ipv4", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "export", # "route_map": "map01" # }, # { # "action": "import", # "route_map": "map01" # } # ], # "weight": 50 # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list", # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate", # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged", # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number 4", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map map01", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export 10", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix 45", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self" # ], # Using overridden # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast network 35.1.1.0/24 backdoor # set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' # set protocols bgp 100 address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only # set protocols bgp 100 address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10' # vyos@vyos:~$ - name: Override vyos.vyos.vyos_bgp_address_family: config: as_number: "100" neighbors: - neighbor_address: "100.11.34.12" address_family: - afi: "ipv6" maximum_prefix: 45 nexthop_self: True route_map: - action: "import" route_map: "map01" address_family: - afi: "ipv4" aggregate_address: - prefix: "60.9.2.0/24" summary_only: True - afi: "ipv6" redistribute: - protocol: "static" metric: 50 state: overridden # Aft=validate-moduleser State # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only # set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01' # vyos@vyos:~$ # Module Execution: # "after": { # "address_family": [ # { # "afi": "ipv4", # "aggregate_address": [ # { # "prefix": "60.9.2.0/24", # "summary_only": true # } # ] # }, # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4" # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "import", # "route_map": "map01" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # } # ] # }, # "before": { # "address_family": [ # { # "afi": "ipv4", # "networks": [ # { # "backdoor": true, # "prefix": "35.1.1.0/24" # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # }, # { # "afi": "ipv6", # "aggregate_address": [ # { # "prefix": "6601:1:1:1::/64", # "summary_only": true # } # ], # "networks": [ # { # "prefix": "5001:1:1:1::/64", # "route_map": "map01" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4" # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in", # "delete protocols bgp 100 address-family ipv6 aggregate-address", # "delete protocols bgp 100 address-family ipv6 network", # "delete protocols bgp 100 address-family ipv4 network", # "delete protocols bgp 100 address-family ipv4 redistribute", # "set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only", # "set protocols bgp 100 address-family ipv6-unicast redistribute static metric 50", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix 45", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import map01" # ], # # Using deleted: # Before State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only # set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' # set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45' # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self # set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01' # vyos@vyos:~$ - name: Delete vyos.vyos.vyos_bgp_address_family: config: as_number: "100" neighbors: - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv6" - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" state: deleted # After State: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 100.11.34.12 # vyos@vyos:~$ # # # Module Execution: # # "after": { # "address_family": [ # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "before": { # "address_family": [ # { # "afi": "ipv4", # "aggregate_address": [ # { # "prefix": "60.9.2.0/24", # "summary_only": true # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # }, # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "export", # "route_map": "map01" # }, # { # "action": "import", # "route_map": "map01" # } # ], # "weight": 50 # }, # { # "afi": "ipv6", # "maximum_prefix": 45, # "nexthop_self": true, # "route_map": [ # { # "action": "import", # "route_map": "map01" # } # ] # } # ], # "neighbor_address": "100.11.34.12" # }, # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # }, # { # "afi": "ipv6", # "default_originate": "map01", # "distribute_list": [ # { # "acl": 10, # "action": "export" # } # ] # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp 100 address-family ipv4-unicast", # "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast", # "delete protocols bgp 100 neighbor 100.11.34.12 address-family" # ], # # using parsed: # parsed.cfg # set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set # set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map 'map01' # set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor # set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20' # set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export 'map01' # set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound # set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop - name: parse configs vyos.vyos.vyos_bgp_address_family: running_config: "{{ lookup('file', './parsed.cfg') }}" state: parsed # Module Execution: # "parsed": { # "address_family": [ # { # "afi": "ipv4", # "aggregate_address": [ # { # "as_set": true, # "prefix": "192.0.2.0/24" # } # ], # "networks": [ # { # "prefix": "192.1.13.0/24", # "route_map": "map01" # }, # { # "backdoor": true, # "prefix": "192.2.13.0/24" # } # ] # }, # { # "afi": "ipv6", # "redistribute": [ # { # "metric": 20, # "protocol": "ripng" # } # ] # } # ], # "as_number": 65536, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "route_map": [ # { # "action": "export", # "route_map": "map01" # } # ], # "soft_reconfiguration": true # } # ], # "neighbor_address": "192.0.2.25" # }, # { # "address_family": [ # { # "afi": "ipv6", # "attribute_unchanged": { # "next_hop": true # } # } # ], # "neighbor_address": "203.0.113.5" # } # ] # # Using gathered: # Native config: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 100 address-family ipv4-unicast network 35.1.1.0/24 backdoor # set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50' # set protocols bgp 100 address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only # set protocols bgp 100 address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01' # set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4' # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override # set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med # set protocols bgp 100 neighbor 100.11.34.12 - name: gather configs vyos.vyos.vyos_bgp_address_family: state: gathered # Module Execution: # "gathered": { # "address_family": [ # { # "afi": "ipv4", # "networks": [ # { # "backdoor": true, # "prefix": "35.1.1.0/24" # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # }, # { # "afi": "ipv6", # "aggregate_address": [ # { # "prefix": "6601:1:1:1::/64", # "summary_only": true # } # ], # "networks": [ # { # "prefix": "5001:1:1:1::/64", # "route_map": "map01" # } # ], # "redistribute": [ # { # "metric": 50, # "protocol": "static" # } # ] # } # ], # "as_number": 100, # "neighbors": [ # { # "address_family": [ # { # "afi": "ipv4", # "allowas_in": 4, # "as_override": true, # "attribute_unchanged": { # "med": true # } # } # ], # "neighbor_address": "20.33.1.1/24" # } # ] # Using rendered: - name: Render vyos.vyos.vyos_bgp_address_family: config: as_number: "100" address_family: - afi: "ipv4" redistribute: - protocol: "static" metric: 50 neighbors: - neighbor_address: "20.33.1.1/24" address_family: - afi: "ipv4" allowas_in: 4 as_override: True attribute_unchanged: med: True - afi: "ipv6" default_originate: "map01" distribute_list: - action: "export" acl: 10 - neighbor_address: "100.11.34.12" address_family: - afi: "ipv4" maximum_prefix: 45 nexthop_self: True route_map: - action: "export" route_map: "map01" - action: "import" route_map: "map01" weight: 50 state: rendered # Module Execution: # "rendered": [ # "set protocols bgp 100 address-family ipv4-unicast redistribute static metric 50", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01", # "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01", # "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight 50" # ] """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import ( Bgp_address_familyArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family import ( Bgp_address_family, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Bgp_address_familyArgs.argument_spec, mutually_exclusive=[], required_if=[], supports_check_mode=False, ) result = Bgp_address_family(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_bgp_global.py b/plugins/modules/vyos_bgp_global.py index 49e0a72..1e3578d 100644 --- a/plugins/modules/vyos_bgp_global.py +++ b/plugins/modules/vyos_bgp_global.py @@ -1,1440 +1,1441 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_bgp_global """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_bgp_global version_added: 2.0.0 short_description: BGP Global Resource Module. description: - This module manages BGP global configuration of interfaces on devices running VYOS. author: Gomathi Selvi Srinivasan (@GomathiselviS) options: config: description: A dict of BGP global configuration for interfaces. type: dict suboptions: as_number: description: - AS number. type: int aggregate_address: description: - BGP aggregate network. type: list elements: dict suboptions: prefix: description: BGP aggregate network. type: str as_set: description: Generate AS-set path information for this aggregate address. type: bool summary_only: description: Announce the aggregate summary network only. type: bool maximum_paths: description: BGP multipaths type: list elements: dict suboptions: path: description: BGP multipaths type: str count: description: No. of paths. type: int neighbor: description: BGP neighbor type: list elements: dict suboptions: address: description: BGP neighbor address (v4/v6). type: str advertisement_interval: description: Minimum interval for sending routing updates. type: int allowas_in: description: Number of occurrences of AS number. type: int as_override: description: AS for routes sent to this neighbor to be the local AS. type: bool attribute_unchanged: description: BGP attributes are sent unchanged. type: dict suboptions: as_path: description: as_path type: bool med: description: med type: bool next_hop: description: next_hop type: bool capability: description: Advertise capabilities to this neighbor. type: dict suboptions: dynamic: description: Advertise dynamic capability to this neighbor. type: bool orf: description: Advertise ORF capability to this neighbor. type: str choices: ['send', 'receive'] default_originate: description: Send default route to this neighbor type: str description: description: description text type: str disable_capability_negotiation: description: Disbale capability negotiation with the neighbor type: bool disable_connected_check: description: Disable check to see if EBGP peer's address is a connected route. type: bool disable_send_community: description: Disable sending community attributes to this neighbor. type: str choices: ['extended', 'standard'] distribute_list: description: Access-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: Access-list to filter outgoing/incoming route updates to this neighbor type: str choices: ['export', 'import'] acl: description: Access-list number. type: int ebgp_multihop: description: Allow this EBGP neighbor to not be on a directly connected network. Specify the number hops. type: int filter_list: description: As-path-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] path_list: description: As-path-list to filter type: str local_as: description: local as number not to be prepended to updates from EBGP peers type: int maximum_prefix: description: Maximum number of prefixes to accept from this neighbor nexthop-self Nexthop for routes sent to this neighbor to be the local router. type: int nexthop_self: description: Nexthop for routes sent to this neighbor to be the local router. type: bool override_capability: description: Ignore capability negotiation with specified neighbor. type: bool passive: description: Do not initiate a session with this neighbor type: bool password: description: BGP MD5 password type: str peer_group_name: description: IPv4 peer group for this peer type: str peer_group: description: True if all the configs under this neighbor key is for peer group template. type: bool port: description: Neighbor's BGP port type: int prefix_list: description: Prefix-list to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] prefix_list: description: Prefix-list to filter type: str remote_as: description: Neighbor BGP AS number type: int remove_private_as: description: Remove private AS numbers from AS path in outbound route updates type: bool route_map: description: Route-map to filter route updates to/from this neighbor. type: list elements: dict suboptions: action: description: filter outgoing/incoming route updates type: str choices: ['export', 'import'] route_map: description: route-map to filter type: str route_reflector_client: description: Neighbor as a route reflector client type: bool route_server_client: description: Neighbor is route server client type: bool shutdown: description: Administratively shut down neighbor type: bool soft_reconfiguration: description: Soft reconfiguration for neighbor type: bool strict_capability_match: description: Enable strict capability negotiation type: bool unsuppress_map: description: Route-map to selectively unsuppress suppressed routes type: str update_source: description: Source IP of routing updates type: str weight: description: Default weight for routes from this neighbor type: int ttl_security: description: Ttl security mechanism for this BGP peer type: int timers: description: Neighbor timers type: dict suboptions: connect: description: BGP connect timer for this neighbor. type: int holdtime: description: BGP hold timer for this neighbor type: int keepalive: description: BGP keepalive interval for this neighbor type: int network: description: BGP network type: list elements: dict suboptions: address: description: BGP network address type: str backdoor: description: Network as a backdoor route type: bool route_map: description: Route-map to modify route attributes type: str redistribute: description: Redistribute routes from other protocols into BGP type: list elements: dict suboptions: protocol: description: types of routes to be redistributed. type: str choices: ['connected', 'kernel', 'ospf', 'rip', 'static'] route_map: description: Route map to filter redistributed routes type: str metric: description: Metric for redistributed routes. type: int timers: description: BGP protocol timers type: dict suboptions: keepalive: description: Keepalive interval type: int holdtime: description: Hold time interval type: int bgp_params: description: BGP parameters type: dict suboptions: always_compare_med: description: Always compare MEDs from different neighbors type: bool bestpath: description: Default bestpath selection mechanism type: dict suboptions: as_path: description: AS-path attribute comparison parameters type: str choices: ['confed', 'ignore'] compare_routerid: description: Compare the router-id for identical EBGP paths type: bool med: description: MED attribute comparison parameters type: str choices: ['confed', 'missing-as-worst'] cluster_id: description: Route-reflector cluster-id type: str confederation: description: AS confederation parameters type: list elements: dict suboptions: identifier: description: Confederation AS identifier type: int peers: description: Peer ASs in the BGP confederation type: int dampening: description: Enable route-flap dampening type: dict suboptions: half_life: description: Half-life penalty in seconds type: int max_suppress_time: description: Maximum duration to suppress a stable route type: int re_use: description: Time to start reusing a route type: int start_suppress_time: description: When to start suppressing a route type: int default: description: BGP defaults type: dict suboptions: local_pref: description: Default local preference type: int no_ipv4_unicast: description: Deactivate IPv4 unicast for a peer by default type: bool deterministic_med: description: Compare MEDs between different peers in the same AS type: bool disable_network_import_check: description: Disable IGP route check for network statements type: bool distance: description: Administrative distances for BGP routes type: list elements: dict suboptions: type: description: Type of route type: str choices: ['external', 'internal', 'local'] value: description: distance type: int prefix: description: Administrative distance for a specific BGP prefix type: int enforce_first_as: description: Require first AS in the path to match peer's AS type: bool graceful_restart: description: Maximum time to hold onto restarting peer's stale paths type: int log_neighbor_changes: description: Log neighbor up/down changes and reset reason type: bool no_client_to_client_reflection: description: Disable client to client route reflection type: bool no_fast_external_failover: description: Disable immediate session reset if peer's connected link goes down type: bool router_id: description: BGP router-id type: str scan_time: description: BGP route scanner interval type: int state: description: - The state the configuration should be left in. - State I(purged) removes all the BGP configurations from the target device. Use caution with this state.('delete protocols bgp ') - State I(deleted) only removes BGP attributes that this modules manages and does not negate the BGP process completely. Thereby, preserving address-family related configurations under BGP context. - Running states I(deleted) and I(replaced) will result in an error if there are address-family configuration lines present under neighbor context that is is to be removed. Please use the M(vyos.vyos.vyos_bgp_address_family) module for prior cleanup. - Refer to examples for more details. type: str choices: [deleted, merged, purged, replaced, gathered, rendered, parsed] default: merged running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the EOS device by executing the command B(show running-config | section bgp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str """ EXAMPLES = """ # Using merged # Before state # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # vyos@vyos:~$ - name: Merge provided configuration with device configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" aggregate_address: - prefix: "203.0.113.0/24" as_set: true - prefix: "192.0.2.0/24" summary_only: true network: - address: "192.1.13.0/24" backdoor: true redistribute: - protocol: "kernel" metric: 45 - protocol: "connected" route_map: "map01" maximum_paths: - path: "ebgp" count: 20 - path: "ibgp" count: 55 timers: keepalive: 35 bgp_params: bestpath: as_path: "confed" compare_routerid: true default: no_ipv4_unicast: true router_id: "192.1.2.9" confederation: - peers: 20 - peers: 55 - identifier: 66 neighbor: - address: "192.0.2.25" disable_connected_check: true timers: holdtime: 30 keepalive: 10 - address: "203.0.113.5" attribute_unchanged: as_path: true med: true ebgp_multihop: 2 remote_as: 101 update_source: "192.0.2.25" - address: "5001::64" maximum_prefix: 34 distribute_list: - acl: 20 action: "export" - acl: 40 action: "import" state: merged # After State # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 aggregate-address 192.0.2.0/24 'summary-only' # set protocols bgp 65536 aggregate-address 203.0.113.0/24 'as-set' # set protocols bgp 65536 maximum-paths ebgp '20' # set protocols bgp 65536 maximum-paths ibgp '55' # set protocols bgp 65536 neighbor 192.0.2.25 'disable-connected-check' # set protocols bgp 65536 neighbor 192.0.2.25 timers holdtime '30' # set protocols bgp 65536 neighbor 192.0.2.25 timers keepalive '10' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'as-path' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'med' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'next-hop' # set protocols bgp 65536 neighbor 203.0.113.5 ebgp-multihop '2' # set protocols bgp 65536 neighbor 203.0.113.5 remote-as '101' # set protocols bgp 65536 neighbor 203.0.113.5 update-source '192.0.2.25' # set protocols bgp 65536 neighbor 5001::64 distribute-list export '20' # set protocols bgp 65536 neighbor 5001::64 distribute-list import '40' # set protocols bgp 65536 neighbor 5001::64 maximum-prefix '34' # set protocols bgp 65536 network 192.1.13.0/24 'backdoor' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 parameters bestpath 'compare-routerid' # set protocols bgp 65536 parameters confederation identifier '66' # set protocols bgp 65536 parameters confederation peers '20' # set protocols bgp 65536 parameters confederation peers '55' # set protocols bgp 65536 parameters default 'no-ipv4-unicast' # set protocols bgp 65536 parameters router-id '192.1.2.9' # set protocols bgp 65536 redistribute connected route-map 'map01' # set protocols bgp 65536 redistribute kernel metric '45' # set protocols bgp 65536 timers keepalive '35' # vyos@vyos:~$ # # # Module Execution: # # "after": { # "aggregate_address": [ # { # "prefix": "192.0.2.0/24", # "summary_only": true # }, # { # "prefix": "203.0.113.0/24", # "as_set": true # } # ], # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "confederation": [ # { # "identifier": 66 # }, # { # "peers": 20 # }, # { # "peers": 55 # } # ], # "default": { # "no_ipv4_unicast": true # }, # "router_id": "192.1.2.9" # }, # "maximum_paths": [ # { # "count": 20, # "path": "ebgp" # }, # { # "count": 55, # "path": "ibgp" # } # ], # "neighbor": [ # { # "address": "192.0.2.25", # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.5", # "attribute_unchanged": { # "as_path": true, # "med": true, # "next_hop": true # }, # "ebgp_multihop": 2, # "remote_as": 101, # "update_source": "192.0.2.25" # }, # { # "address": "5001::64", # "distribute_list": [ # { # "acl": 20, # "action": "export" # }, # { # "acl": 40, # "action": "import" # } # ], # "maximum_prefix": 34 # } # ], # "network": [ # { # "address": "192.1.13.0/24", # "backdoor": true # } # ], # "redistribute": [ # { # "protocol": "connected", # "route_map": "map01" # }, # { # "metric": 45, # "protocol": "kernel" # } # ], # "timers": { # "keepalive": 35 # } # }, # "before": {}, # "changed": true, # "commands": [ # "set protocols bgp 65536 neighbor 192.0.2.25 disable-connected-check", # "set protocols bgp 65536 neighbor 192.0.2.25 timers holdtime 30", # "set protocols bgp 65536 neighbor 192.0.2.25 timers keepalive 10", # "set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged as-path", # "set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged med", # "set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged next-hop", # "set protocols bgp 65536 neighbor 203.0.113.5 ebgp-multihop 2", # "set protocols bgp 65536 neighbor 203.0.113.5 remote-as 101", # "set protocols bgp 65536 neighbor 203.0.113.5 update-source 192.0.2.25", # "set protocols bgp 65536 neighbor 5001::64 maximum-prefix 34", # "set protocols bgp 65536 neighbor 5001::64 distribute-list export 20", # "set protocols bgp 65536 neighbor 5001::64 distribute-list import 40", # "set protocols bgp 65536 redistribute kernel metric 45", # "set protocols bgp 65536 redistribute connected route-map map01", # "set protocols bgp 65536 network 192.1.13.0/24 backdoor", # "set protocols bgp 65536 aggregate-address 203.0.113.0/24 as-set", # "set protocols bgp 65536 aggregate-address 192.0.2.0/24 summary-only", # "set protocols bgp 65536 parameters bestpath as-path confed", # "set protocols bgp 65536 parameters bestpath compare-routerid", # "set protocols bgp 65536 parameters default no-ipv4-unicast", # "set protocols bgp 65536 parameters router-id 192.1.2.9", # "set protocols bgp 65536 parameters confederation peers 20", # "set protocols bgp 65536 parameters confederation peers 55", # "set protocols bgp 65536 parameters confederation identifier 66", # "set protocols bgp 65536 maximum-paths ebgp 20", # "set protocols bgp 65536 maximum-paths ibgp 55", # "set protocols bgp 65536 timers keepalive 35" # ], # Using replaced: # -------------- # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 aggregate-address 192.0.2.0/24 'summary-only' # set protocols bgp 65536 aggregate-address 203.0.113.0/24 'as-set' # set protocols bgp 65536 maximum-paths ebgp '20' # set protocols bgp 65536 maximum-paths ibgp '55' # set protocols bgp 65536 neighbor 192.0.2.25 'disable-connected-check' # set protocols bgp 65536 neighbor 192.0.2.25 timers holdtime '30' # set protocols bgp 65536 neighbor 192.0.2.25 timers keepalive '10' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'as-path' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'med' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'next-hop' # set protocols bgp 65536 neighbor 203.0.113.5 ebgp-multihop '2' # set protocols bgp 65536 neighbor 203.0.113.5 remote-as '101' # set protocols bgp 65536 neighbor 203.0.113.5 update-source '192.0.2.25' # set protocols bgp 65536 neighbor 5001::64 distribute-list export '20' # set protocols bgp 65536 neighbor 5001::64 distribute-list import '40' # set protocols bgp 65536 neighbor 5001::64 maximum-prefix '34' # set protocols bgp 65536 network 192.1.13.0/24 'backdoor' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 parameters bestpath 'compare-routerid' # set protocols bgp 65536 parameters confederation identifier '66' # set protocols bgp 65536 parameters confederation peers '20' # set protocols bgp 65536 parameters confederation peers '55' # set protocols bgp 65536 parameters default 'no-ipv4-unicast' # set protocols bgp 65536 parameters router-id '192.1.2.9' # set protocols bgp 65536 redistribute connected route-map 'map01' # set protocols bgp 65536 redistribute kernel metric '45' # set protocols bgp 65536 timers keepalive '35' # vyos@vyos:~$ - name: Replace vyos.vyos.vyos_bgp_global: config: as_number: "65536" network: - address: "203.0.113.0/24" route_map: map01 redistribute: - protocol: "static" route_map: "map01" neighbor: - address: "192.0.2.40" advertisement_interval: 72 capability: orf: "receive" bgp_params: bestpath: as_path: "confed" state: replaced # After state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 neighbor 192.0.2.40 advertisement-interval '72' # set protocols bgp 65536 neighbor 192.0.2.40 capability orf prefix-list 'receive' # set protocols bgp 65536 network 203.0.113.0/24 route-map 'map01' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 redistribute static route-map 'map01' # vyos@vyos:~$ # # # Module Execution: # # "after": { # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed" # } # }, # "neighbor": [ # { # "address": "192.0.2.40", # "advertisement_interval": 72, # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # }, # "before": { # "aggregate_address": [ # { # "prefix": "192.0.2.0/24", # "summary_only": true # }, # { # "prefix": "203.0.113.0/24", # "as_set": true # } # ], # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "confederation": [ # { # "identifier": 66 # }, # { # "peers": 20 # }, # { # "peers": 55 # } # ], # "default": { # "no_ipv4_unicast": true # }, # "router_id": "192.1.2.9" # }, # "maximum_paths": [ # { # "count": 20, # "path": "ebgp" # }, # { # "count": 55, # "path": "ibgp" # } # ], # "neighbor": [ # { # "address": "192.0.2.25", # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.5", # "attribute_unchanged": { # "as_path": true, # "med": true, # "next_hop": true # }, # "ebgp_multihop": 2, # "remote_as": 101, # "update_source": "192.0.2.25" # }, # { # "address": "5001::64", # "distribute_list": [ # { # "acl": 20, # "action": "export" # }, # { # "acl": 40, # "action": "import" # } # ], # "maximum_prefix": 34 # } # ], # "network": [ # { # "address": "192.1.13.0/24", # "backdoor": true # } # ], # "redistribute": [ # { # "protocol": "connected", # "route_map": "map01" # }, # { # "metric": 45, # "protocol": "kernel" # } # ], # "timers": { # "keepalive": 35 # } # }, # "changed": true, # "commands": [ # "delete protocols bgp 65536 timers", # "delete protocols bgp 65536 maximum-paths ", # "delete protocols bgp 65536 maximum-paths ", # "delete protocols bgp 65536 parameters router-id 192.1.2.9", # "delete protocols bgp 65536 parameters default", # "delete protocols bgp 65536 parameters confederation", # "delete protocols bgp 65536 parameters bestpath compare-routerid", # "delete protocols bgp 65536 aggregate-address", # "delete protocols bgp 65536 network 192.1.13.0/24", # "delete protocols bgp 65536 redistribute kernel", # "delete protocols bgp 65536 redistribute kernel", # "delete protocols bgp 65536 redistribute connected", # "delete protocols bgp 65536 redistribute connected", # "delete protocols bgp 65536 neighbor 5001::64", # "delete protocols bgp 65536 neighbor 203.0.113.5", # "delete protocols bgp 65536 neighbor 192.0.2.25", # "set protocols bgp 65536 neighbor 192.0.2.40 advertisement-interval 72", # "set protocols bgp 65536 neighbor 192.0.2.40 capability orf prefix-list receive", # "set protocols bgp 65536 redistribute static route-map map01", # "set protocols bgp 65536 network 203.0.113.0/24 route-map map01" # ], # Using deleted: # ------------- # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 neighbor 192.0.2.40 advertisement-interval '72' # set protocols bgp 65536 neighbor 192.0.2.40 capability orf prefix-list 'receive' # set protocols bgp 65536 network 203.0.113.0/24 route-map 'map01' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 redistribute static route-map 'map01' # vyos@vyos:~$ - name: Delete configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" state: deleted # After state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp '65536' # vyos@vyos:~$ # # # Module Execution: # # "after": { # "as_number": 65536 # }, # "before": { # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed" # } # }, # "neighbor": [ # { # "address": "192.0.2.40", # "advertisement_interval": 72, # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # }, # "changed": true, # "commands": [ # "delete protocols bgp 65536 neighbor 192.0.2.40", # "delete protocols bgp 65536 redistribute", # "delete protocols bgp 65536 network", # "delete protocols bgp 65536 parameters" # ], # Using purged: # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 aggregate-address 192.0.2.0/24 'summary-only' # set protocols bgp 65536 aggregate-address 203.0.113.0/24 'as-set' # set protocols bgp 65536 maximum-paths ebgp '20' # set protocols bgp 65536 maximum-paths ibgp '55' # set protocols bgp 65536 neighbor 192.0.2.25 'disable-connected-check' # set protocols bgp 65536 neighbor 192.0.2.25 timers holdtime '30' # set protocols bgp 65536 neighbor 192.0.2.25 timers keepalive '10' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'as-path' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'med' # set protocols bgp 65536 neighbor 203.0.113.5 attribute-unchanged 'next-hop' # set protocols bgp 65536 neighbor 203.0.113.5 ebgp-multihop '2' # set protocols bgp 65536 neighbor 203.0.113.5 remote-as '101' # set protocols bgp 65536 neighbor 203.0.113.5 update-source '192.0.2.25' # set protocols bgp 65536 neighbor 5001::64 distribute-list export '20' # set protocols bgp 65536 neighbor 5001::64 distribute-list import '40' # set protocols bgp 65536 neighbor 5001::64 maximum-prefix '34' # set protocols bgp 65536 network 192.1.13.0/24 'backdoor' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 parameters bestpath 'compare-routerid' # set protocols bgp 65536 parameters confederation identifier '66' # set protocols bgp 65536 parameters confederation peers '20' # set protocols bgp 65536 parameters confederation peers '55' # set protocols bgp 65536 parameters default 'no-ipv4-unicast' # set protocols bgp 65536 parameters router-id '192.1.2.9' # set protocols bgp 65536 redistribute connected route-map 'map01' # set protocols bgp 65536 redistribute kernel metric '45' # set protocols bgp 65536 timers keepalive '35' # vyos@vyos:~$ - name: Purge configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" state: purged # After state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # vyos@vyos:~$ # # Module Execution: # # "after": {}, # "before": { # "aggregate_address": [ # { # "prefix": "192.0.2.0/24", # "summary_only": true # }, # { # "prefix": "203.0.113.0/24", # "as_set": true # } # ], # "as_number": 65536, # "bgp_params": { # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "confederation": [ # { # "identifier": 66 # }, # { # "peers": 20 # }, # { # "peers": 55 # } # ], # "default": { # "no_ipv4_unicast": true # }, # "router_id": "192.1.2.9" # }, # "maximum_paths": [ # { # "count": 20, # "path": "ebgp" # }, # { # "count": 55, # "path": "ibgp" # } # ], # "neighbor": [ # { # "address": "192.0.2.25", # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.5", # "attribute_unchanged": { # "as_path": true, # "med": true, # "next_hop": true # }, # "ebgp_multihop": 2, # "remote_as": 101, # "update_source": "192.0.2.25" # }, # { # "address": "5001::64", # "distribute_list": [ # { # "acl": 20, # "action": "export" # }, # { # "acl": 40, # "action": "import" # } # ], # "maximum_prefix": 34 # } # ], # "network": [ # { # "address": "192.1.13.0/24", # "backdoor": true # } # ], # "redistribute": [ # { # "protocol": "connected", # "route_map": "map01" # }, # { # "metric": 45, # "protocol": "kernel" # } # ], # "timers": { # "keepalive": 35 # } # }, # "changed": true, # "commands": [ # "delete protocols bgp 65536" # ], # Deleted in presence of address family under neighbors: # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 neighbor 192.0.2.43 advertisement-interval '72' # set protocols bgp 65536 neighbor 192.0.2.43 capability 'dynamic' # set protocols bgp 65536 neighbor 192.0.2.43 'disable-connected-check' # set protocols bgp 65536 neighbor 192.0.2.43 timers holdtime '30' # set protocols bgp 65536 neighbor 192.0.2.43 timers keepalive '10' # set protocols bgp 65536 neighbor 203.0.113.0 address-family 'ipv6-unicast' # set protocols bgp 65536 neighbor 203.0.113.0 capability orf prefix-list 'receive' # set protocols bgp 65536 network 203.0.113.0/24 route-map 'map01' # set protocols bgp 65536 parameters 'always-compare-med' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 parameters bestpath 'compare-routerid' # set protocols bgp 65536 parameters dampening half-life '33' # set protocols bgp 65536 parameters dampening max-suppress-time '20' # set protocols bgp 65536 parameters dampening re-use '60' # set protocols bgp 65536 parameters dampening start-suppress-time '5' # set protocols bgp 65536 parameters default 'no-ipv4-unicast' # set protocols bgp 65536 parameters distance global external '66' # set protocols bgp 65536 parameters distance global internal '20' # set protocols bgp 65536 parameters distance global local '10' # set protocols bgp 65536 redistribute static route-map 'map01' # vyos@vyos:~$ ^C # vyos@vyos:~$ - name: Delete configuration vyos.vyos.vyos_bgp_global: config: as_number: "65536" state: deleted # Module Execution: # # "changed": false, # "invocation": { # "module_args": { # "config": { # "aggregate_address": null, # "as_number": 65536, # "bgp_params": null, # "maximum_paths": null, # "neighbor": null, # "network": null, # "redistribute": null, # "timers": null # }, # "running_config": null, # "state": "deleted" # } # }, # "msg": "Use the _bgp_address_family module to delete the address_family under neighbor 203.0.113.0, before replacing/deleting the neighbor." # } # using gathered: # -------------- # Before state: # vyos@vyos:~$ show configuration commands | match "set protocols bgp" # set protocols bgp 65536 neighbor 192.0.2.43 advertisement-interval '72' # set protocols bgp 65536 neighbor 192.0.2.43 capability 'dynamic' # set protocols bgp 65536 neighbor 192.0.2.43 'disable-connected-check' # set protocols bgp 65536 neighbor 192.0.2.43 timers holdtime '30' # set protocols bgp 65536 neighbor 192.0.2.43 timers keepalive '10' # set protocols bgp 65536 neighbor 203.0.113.0 address-family 'ipv6-unicast' # set protocols bgp 65536 neighbor 203.0.113.0 capability orf prefix-list 'receive' # set protocols bgp 65536 network 203.0.113.0/24 route-map 'map01' # set protocols bgp 65536 parameters 'always-compare-med' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 parameters bestpath 'compare-routerid' # set protocols bgp 65536 parameters dampening half-life '33' # set protocols bgp 65536 parameters dampening max-suppress-time '20' # set protocols bgp 65536 parameters dampening re-use '60' # set protocols bgp 65536 parameters dampening start-suppress-time '5' # set protocols bgp 65536 parameters default 'no-ipv4-unicast' # set protocols bgp 65536 parameters distance global external '66' # set protocols bgp 65536 parameters distance global internal '20' # set protocols bgp 65536 parameters distance global local '10' # set protocols bgp 65536 redistribute static route-map 'map01' # vyos@vyos:~$ ^C - name: gather configs vyos.vyos.vyos_bgp_global: state: gathered # Module Execution: # "gathered": { # "as_number": 65536, # "bgp_params": { # "always_compare_med": true, # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "default": { # "no_ipv4_unicast": true # }, # "distance": [ # { # "type": "external", # "value": 66 # }, # { # "type": "internal", # "value": 20 # }, # { # "type": "local", # "value": 10 # } # ] # }, # "neighbor": [ # { # "address": "192.0.2.43", # "advertisement_interval": 72, # "capability": { # "dynamic": true # }, # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.0", # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # }, # # Using parsed: # ------------ # parsed.cfg # set protocols bgp 65536 neighbor 192.0.2.43 advertisement-interval '72' # set protocols bgp 65536 neighbor 192.0.2.43 capability 'dynamic' # set protocols bgp 65536 neighbor 192.0.2.43 'disable-connected-check' # set protocols bgp 65536 neighbor 192.0.2.43 timers holdtime '30' # set protocols bgp 65536 neighbor 192.0.2.43 timers keepalive '10' # set protocols bgp 65536 neighbor 203.0.113.0 address-family 'ipv6-unicast' # set protocols bgp 65536 neighbor 203.0.113.0 capability orf prefix-list 'receive' # set protocols bgp 65536 network 203.0.113.0/24 route-map 'map01' # set protocols bgp 65536 parameters 'always-compare-med' # set protocols bgp 65536 parameters bestpath as-path 'confed' # set protocols bgp 65536 parameters bestpath 'compare-routerid' # set protocols bgp 65536 parameters dampening half-life '33' # set protocols bgp 65536 parameters dampening max-suppress-time '20' # set protocols bgp 65536 parameters dampening re-use '60' # set protocols bgp 65536 parameters dampening start-suppress-time '5' # set protocols bgp 65536 parameters default 'no-ipv4-unicast' # set protocols bgp 65536 parameters distance global external '66' # set protocols bgp 65536 parameters distance global internal '20' # set protocols bgp 65536 parameters distance global local '10' # set protocols bgp 65536 redistribute static route-map 'map01' - name: parse configs vyos.vyos.vyos_bgp_global: running_config: "{{ lookup('file', './parsed.cfg') }}" state: parsed tags: - parsed # Module execution: # "parsed": { # "as_number": 65536, # "bgp_params": { # "always_compare_med": true, # "bestpath": { # "as_path": "confed", # "compare_routerid": true # }, # "default": { # "no_ipv4_unicast": true # }, # "distance": [ # { # "type": "external", # "value": 66 # }, # { # "type": "internal", # "value": 20 # }, # { # "type": "local", # "value": 10 # } # ] # }, # "neighbor": [ # { # "address": "192.0.2.43", # "advertisement_interval": 72, # "capability": { # "dynamic": true # }, # "disable_connected_check": true, # "timers": { # "holdtime": 30, # "keepalive": 10 # } # }, # { # "address": "203.0.113.0", # "capability": { # "orf": "receive" # } # } # ], # "network": [ # { # "address": "203.0.113.0/24", # "route_map": "map01" # } # ], # "redistribute": [ # { # "protocol": "static", # "route_map": "map01" # } # ] # } # # Using rendered: # -------------- - name: Render vyos.vyos.vyos_bgp_global: config: as_number: "65536" network: - address: "203.0.113.0/24" route_map: map01 redistribute: - protocol: "static" route_map: "map01" bgp_params: always_compare_med: true dampening: start_suppress_time: 5 max_suppress_time: 20 half_life: 33 re_use: 60 distance: - type: "internal" value: 20 - type: "local" value: 10 - type: "external" value: 66 bestpath: as_path: "confed" compare_routerid: true default: no_ipv4_unicast: true neighbor: - address: "192.0.2.43" disable_connected_check: true advertisement_interval: 72 capability: dynamic: true timers: holdtime: 30 keepalive: 10 - address: "203.0.113.0" capability: orf: "receive" state: rendered # Module Execution: # "rendered": [ # "set protocols bgp 65536 neighbor 192.0.2.43 disable-connected-check", # "set protocols bgp 65536 neighbor 192.0.2.43 advertisement-interval 72", # "set protocols bgp 65536 neighbor 192.0.2.43 capability dynamic", # "set protocols bgp 65536 neighbor 192.0.2.43 timers holdtime 30", # "set protocols bgp 65536 neighbor 192.0.2.43 timers keepalive 10", # "set protocols bgp 65536 neighbor 203.0.113.0 capability orf prefix-list receive", # "set protocols bgp 65536 redistribute static route-map map01", # "set protocols bgp 65536 network 203.0.113.0/24 route-map map01", # "set protocols bgp 65536 parameters always-compare-med", # "set protocols bgp 65536 parameters dampening half-life 33", # "set protocols bgp 65536 parameters dampening max-suppress-time 20", # "set protocols bgp 65536 parameters dampening re-use 60", # "set protocols bgp 65536 parameters dampening start-suppress-time 5", # "set protocols bgp 65536 parameters distance global internal 20", # "set protocols bgp 65536 parameters distance global local 10", # "set protocols bgp 65536 parameters distance global external 66", # "set protocols bgp 65536 parameters bestpath as-path confed", # "set protocols bgp 65536 parameters bestpath compare-routerid", # "set protocols bgp 65536 parameters default no-ipv4-unicast" # ] """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_global.bgp_global import ( Bgp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import ( Bgp_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Bgp_globalArgs.argument_spec, mutually_exclusive=[], required_if=[], supports_check_mode=False, ) result = Bgp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_command.py b/plugins/modules/vyos_command.py index d5ab918..94f16f3 100644 --- a/plugins/modules/vyos_command.py +++ b/plugins/modules/vyos_command.py @@ -1,227 +1,224 @@ #!/usr/bin/python # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_command author: Nathaniel Case (@Qalthos) short_description: Run one or more commands on VyOS devices description: - The command module allows running one or more commands on remote devices running VyOS. This module can also be introspected to validate key parameters before returning successfully. If the conditional statements are not met in the wait period, the task fails. - Certain C(show) commands in VyOS produce many lines of output and use a custom pager that can cause this module to hang. If the value of the environment variable C(ANSIBLE_VYOS_TERMINAL_LENGTH) is not set, the default number of 10000 is used. version_added: 1.0.0 extends_documentation_fragment: - vyos.vyos.vyos options: commands: description: - The ordered set of commands to execute on the remote device running VyOS. The output from the command execution is returned to the playbook. If the I(wait_for) argument is provided, the module is not returned until the condition is satisfied or the number of retries has been exceeded. - If a command sent to the device requires answering a prompt, it is possible to pass a dict containing command, answer and prompt. Common answers are 'y' or "\\r" (carriage return, must be double quotes). Refer below examples. required: true type: list elements: raw wait_for: description: - Specifies what to evaluate from the output of the command and what conditionals to apply. This argument will cause the task to wait for a particular conditional to be true before moving forward. If the conditional is not true by the configured I(retries), the task fails. See examples. type: list elements: str aliases: - waitfor match: description: - The I(match) argument is used in conjunction with the I(wait_for) argument to specify the match policy. Valid values are C(all) or C(any). If the value is set to C(all) then all conditionals in the wait_for must be satisfied. If the value is set to C(any) then only one of the values must be satisfied. default: all type: str choices: - any - all retries: description: - Specifies the number of retries a command should be tried before it is considered failed. The command is run on the target device every retry and evaluated against the I(wait_for) conditionals. default: 10 type: int interval: description: - Configures the interval in seconds to wait between I(retries) of the command. If the command does not pass the specified conditions, the interval indicates how long to wait before trying the command again. default: 1 type: int notes: - Tested against VyOS 1.1.8 (helium). - Running C(show system boot-messages all) will cause the module to hang since VyOS is using a custom pager setting to display the output of that command. - If a command sent to the device requires answering a prompt, it is possible to pass a dict containing I(command), I(answer) and I(prompt). See examples. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). """ EXAMPLES = """ - name: show configuration on ethernet devices eth0 and eth1 vyos.vyos.vyos_command: commands: - show interfaces ethernet {{ item }} with_items: - eth0 - eth1 - name: run multiple commands and check if version output contains specific version string vyos.vyos.vyos_command: commands: - show version - show hardware cpu wait_for: - result[0] contains 'VyOS 1.1.7' - name: run command that requires answering a prompt vyos.vyos.vyos_command: commands: - command: rollback 1 prompt: Proceed with reboot? [confirm][y] answer: y """ RETURN = """ stdout: description: The set of responses from the commands returned: always apart from low level errors (such as action plugin) type: list sample: ['...', '...'] stdout_lines: description: The value of stdout split into a list returned: always type: list sample: [['...', '...'], ['...'], ['...']] failed_conditions: description: The list of conditionals that have failed returned: failed type: list sample: ['...', '...'] warnings: description: The list of warnings (if any) generated by module based on arguments returned: always type: list sample: ['...', '...'] """ import time from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import ( Conditional, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - transform_commands, to_lines, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - run_commands, + transform_commands, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import run_commands + def parse_commands(module, warnings): commands = transform_commands(module) if module.check_mode: for item in list(commands): if not item["command"].startswith("show"): warnings.append( "Only show commands are supported when using check mode, not " "executing %s" % item["command"] ) commands.remove(item) return commands def main(): spec = dict( commands=dict(type="list", required=True, elements="raw"), wait_for=dict(type="list", aliases=["waitfor"], elements="str"), match=dict(default="all", choices=["all", "any"]), retries=dict(default=10, type="int"), interval=dict(default=1, type="int"), ) module = AnsibleModule(argument_spec=spec, supports_check_mode=True) warnings = list() result = {"changed": False, "warnings": warnings} commands = parse_commands(module, warnings) wait_for = module.params["wait_for"] or list() try: conditionals = [Conditional(c) for c in wait_for] except AttributeError as exc: module.fail_json(msg=to_text(exc)) retries = module.params["retries"] interval = module.params["interval"] match = module.params["match"] for item in range(retries): responses = run_commands(module, commands) for item in list(conditionals): if item(responses): if match == "any": conditionals = list() break conditionals.remove(item) if not conditionals: break time.sleep(interval) if conditionals: failed_conditions = [item.raw for item in conditionals] msg = "One or more conditional statements have not been satisfied" module.fail_json(msg=msg, failed_conditions=failed_conditions) - result.update( - {"stdout": responses, "stdout_lines": list(to_lines(responses))} - ) + result.update({"stdout": responses, "stdout_lines": list(to_lines(responses))}) module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py index 4b2b31d..2a03d0c 100644 --- a/plugins/modules/vyos_config.py +++ b/plugins/modules/vyos_config.py @@ -1,387 +1,380 @@ #!/usr/bin/python # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_config author: Nathaniel Case (@Qalthos) short_description: Manage VyOS configuration on remote device description: - This module provides configuration file management of VyOS devices. It provides arguments for managing both the configuration file and state of the active configuration. All configuration statements are based on `set` and `delete` commands in the device configuration. version_added: 1.0.0 extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). - To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they appear if present in the running configuration on device including the indentation. options: lines: description: - The ordered set of commands that should be configured in the section. The commands must be the exact same commands as found in the device running-config as found in the device running-config to ensure idempotency and correct diff. Be sure to note the configuration command syntax as some commands are automatically modified by the device config parser. type: list elements: str src: description: - The C(src) argument specifies the path to the source config file to load. The source config file can either be in bracket format or set format. The source file can include Jinja2 template variables. The configuration lines in the source file should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff. type: path match: description: - The C(match) argument controls the method used to match against the current active configuration. By default, the desired config is matched against the active config and the deltas are loaded. If the C(match) argument is set to C(none) the active configuration is ignored and the configuration is always loaded. type: str default: line choices: - line - none backup: description: - The C(backup) argument will backup the current devices active configuration to the Ansible control host prior to making any changes. If the C(backup_options) value is not given, the backup file will be located in the backup folder in the playbook root directory or role root directory, if playbook is part of an ansible role. If the directory does not exist, it is created. type: bool default: no comment: description: - Allows a commit description to be specified to be included when the configuration is committed. If the configuration is not changed or committed, this argument is ignored. default: configured by vyos_config type: str config: description: - The C(config) argument specifies the base configuration to use to compare against the desired configuration. If this value is not specified, the module will automatically retrieve the current active configuration from the remote device. The configuration lines in the option value should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff. type: str save: description: - The C(save) argument controls whether or not changes made to the active configuration are saved to disk. This is independent of committing the config. When set to True, the active configuration is saved. type: bool default: no backup_options: description: - This is a dict object containing configurable options related to backup file path. The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set to I(no) this option will be silently ignored. suboptions: filename: description: - The filename to be used to store the backup configuration. If the filename is not given it will be generated based on the hostname, current time and date in format defined by _config.@ type: str dir_path: description: - This option provides the path ending with directory name in which the backup configuration file will be stored. If the directory does not exist it will be first created and the filename is either the value of C(filename) or default filename as described in C(filename) options description. If the path value is not given in that case a I(backup) directory will be created in the current working directory and backup configuration will be copied in C(filename) within I(backup) directory. type: path type: dict """ EXAMPLES = """ - name: configure the remote device vyos.vyos.vyos_config: lines: - set system host-name {{ inventory_hostname }} - set service lldp - delete service dhcp-server - name: backup and load from file vyos.vyos.vyos_config: src: vyos.cfg backup: yes - name: render a Jinja2 template onto the VyOS router vyos.vyos.vyos_config: src: vyos_template.j2 - name: for idempotency, use full-form commands vyos.vyos.vyos_config: lines: # - set int eth eth2 description 'OUTSIDE' - set interface ethernet eth2 description 'OUTSIDE' - name: configurable backup path vyos.vyos.vyos_config: backup: yes backup_options: filename: backup.cfg dir_path: /home/user """ RETURN = """ commands: description: The list of configuration commands sent to the device returned: always type: list sample: ['...', '...'] filtered: description: The list of configuration commands removed to avoid a load failure returned: always type: list sample: ['...', '...'] backup_path: description: The full path to the backup file returned: when backup is yes type: str sample: /playbooks/ansible/backup/vyos_config.2016-07-16@22:28:34 filename: description: The name of the backup file returned: when backup is yes and filename is not specified in backup options type: str sample: vyos_config.2016-07-16@22:28:34 shortname: description: The full path to the backup file excluding the timestamp returned: when backup is yes and filename is not specified in backup options type: str sample: /playbooks/ansible/backup/vyos_config date: description: The date extracted from the backup file name returned: when backup is yes type: str sample: "2016-07-16" time: description: The time extracted from the backup file name returned: when backup is yes type: str sample: "22:28:34" """ import re from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.connection import ConnectionError + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - load_config, get_config, - run_commands, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_connection, + load_config, + run_commands, ) - DEFAULT_COMMENT = "configured by vyos_config" -CONFIG_FILTERS = [ - re.compile(r"set system login user \S+ authentication encrypted-password") -] +CONFIG_FILTERS = [re.compile(r"set system login user \S+ authentication encrypted-password")] def get_candidate(module): contents = module.params["src"] or module.params["lines"] if module.params["src"]: contents = contents.splitlines() if len(contents) > 0: line = contents[0].split() if len(line) > 0 and line[0] in ("set", "delete"): contents = format_commands(contents) contents = "\n".join(contents) return contents def format_commands(commands): """ This function format the input commands and removes the prepend white spaces for command lines having 'set' or 'delete' and it skips empty lines. :param commands: :return: list of commands """ return [ line.strip() if line.split()[0] in ("set", "delete") else line for line in commands if len(line.strip()) > 0 ] def diff_config(commands, config): config = [str(c).replace("'", "") for c in config.splitlines()] updates = list() visited = set() for line in commands: item = str(line).replace("'", "") if not item.startswith("set") and not item.startswith("delete"): raise ValueError("line must start with either `set` or `delete`") elif item.startswith("set") and item not in config: updates.append(line) elif item.startswith("delete"): if not config: updates.append(line) else: item = re.sub(r"delete", "set", item) for entry in config: if entry.startswith(item) and line not in visited: updates.append(line) visited.add(line) return list(updates) def sanitize_config(config, result): result["filtered"] = list() index_to_filter = list() for regex in CONFIG_FILTERS: for index, line in enumerate(list(config)): if regex.search(line): result["filtered"].append(line) index_to_filter.append(index) # Delete all filtered configs for filter_index in sorted(index_to_filter, reverse=True): del config[filter_index] def run(module, result): # get the current active config from the node or passed in via # the config param config = module.params["config"] or get_config(module) # create the candidate config object from the arguments candidate = get_candidate(module) # create loadable config that includes only the configuration updates connection = get_connection(module) try: response = connection.get_diff( candidate=candidate, running=config, diff_match=module.params["match"], ) except ConnectionError as exc: module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) commands = response.get("config_diff") sanitize_config(commands, result) result["commands"] = commands commit = not module.check_mode comment = module.params["comment"] diff = None if commands: diff = load_config(module, commands, commit=commit, comment=comment) if result.get("filtered"): result["warnings"].append( - "Some configuration commands were " - "removed, please see the filtered key" + "Some configuration commands were removed, please see the filtered key" ) result["changed"] = True if module._diff: result["diff"] = {"prepared": diff} def main(): backup_spec = dict(filename=dict(), dir_path=dict(type="path")) argument_spec = dict( src=dict(type="path"), lines=dict(type="list", elements="str"), match=dict(default="line", choices=["line", "none"]), comment=dict(default=DEFAULT_COMMENT), config=dict(), backup=dict(type="bool", default=False), backup_options=dict(type="dict", options=backup_spec), save=dict(type="bool", default=False), ) mutually_exclusive = [("lines", "src")] module = AnsibleModule( argument_spec=argument_spec, mutually_exclusive=mutually_exclusive, supports_check_mode=True, ) warnings = list() result = dict(changed=False, warnings=warnings) if module.params["backup"]: result["__backup__"] = get_config(module=module) if any((module.params["src"], module.params["lines"])): run(module, result) if module.params["save"]: diff = run_commands(module, commands=["configure", "compare saved"])[1] if diff != "[edit]": if not module.check_mode: run_commands(module, commands=["save"]) result["changed"] = True run_commands(module, commands=["exit"]) - if result.get("changed") and any( - (module.params["src"], module.params["lines"]) - ): + if result.get("changed") and any((module.params["src"], module.params["lines"])): msg = ( "To ensure idempotency and correct diff the input configuration lines should be" " similar to how they appear if present in" " the running configuration on device" ) if module.params["src"]: msg += " including the indentation" if "warnings" in result: result["warnings"].append(msg) else: result["warnings"] = msg module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index 5e57821..ae7543c 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -1,179 +1,176 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type """ The module file for vyos_facts """ DOCUMENTATION = """ module: vyos_facts short_description: Get facts about vyos devices. description: - Collects facts from network devices running the vyos operating system. This module places the facts gathered in the fact tree keyed by the respective resource name. The facts module will always collect a base set of facts from the device and can enable or disable collection of additional facts. version_added: 1.0.0 author: - Nathaniel Case (@qalthos) - Nilashish Chakraborty (@Nilashishc) - Rohit Thakur (@rohitthakur2590) extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: gather_subset: description: - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include C(all), C(default), C(config), C(neighbors) and C(min). Can specify a list of values to include a larger subset. Values can also be used with an initial C(!) to specify that a specific subset should not be collected. required: false default: 'min' type: list elements: str gather_network_resources: description: - When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include all and the resources like interfaces. Can specify a list of values to include a larger subset. Values can also be used with an initial C(!) to specify that a specific subset should not be collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', 'firewall_interfaces', 'ospfv3', 'ospfv2'. required: false type: list elements: str available_network_resources: description: When 'True' a list of network resources for which resource modules are available will be provided. type: bool default: false """ EXAMPLES = """ # Gather all facts - vyos.vyos.vyos_facts: gather_subset: all gather_network_resources: all # collect only the config and default facts - vyos.vyos.vyos_facts: gather_subset: config # collect everything exception the config - vyos.vyos.vyos_facts: gather_subset: '!config' # Collect only the interfaces facts - vyos.vyos.vyos_facts: gather_subset: - '!all' - '!min' gather_network_resources: - interfaces # Do not collect interfaces facts - vyos.vyos.vyos_facts: gather_network_resources: - '!interfaces' # Collect interfaces and minimal default facts - vyos.vyos.vyos_facts: gather_subset: min gather_network_resources: interfaces """ RETURN = """ ansible_net_config: description: The running-config from the device returned: when config is configured type: str ansible_net_commits: description: The set of available configuration revisions returned: when present type: list ansible_net_hostname: description: The configured system hostname returned: always type: str ansible_net_model: description: The device model string returned: always type: str ansible_net_serialnum: description: The serial number of the device returned: always type: str ansible_net_version: description: The version of the software running returned: always type: str ansible_net_neighbors: description: The set of LLDP neighbors returned: when interface is configured type: list ansible_net_gather_subset: description: The list of subsets gathered by the module returned: always type: list ansible_net_api: description: The name of the transport returned: always type: str ansible_net_python_version: description: The Python version Ansible controller is using returned: always type: str ansible_net_gather_network_resources: description: The list of fact resource subsets collected from the device returned: always type: list """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( FactsArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, FACT_RESOURCE_SUBSETS, + Facts, ) def main(): """ Main entry point for module execution :returns: ansible_facts """ argument_spec = FactsArgs.argument_spec - module = AnsibleModule( - argument_spec=argument_spec, supports_check_mode=True - ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) warnings = [] ansible_facts = {} if module.params.get("available_network_resources"): - ansible_facts["available_network_resources"] = sorted( - FACT_RESOURCE_SUBSETS.keys() - ) + ansible_facts["available_network_resources"] = sorted(FACT_RESOURCE_SUBSETS.keys()) result = Facts(module).get_facts() additional_facts, additional_warnings = result ansible_facts.update(additional_facts) warnings.extend(additional_warnings) module.exit_json(ansible_facts=ansible_facts, warnings=warnings) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_global.py b/plugins/modules/vyos_firewall_global.py index 02f4f60..78db9f3 100644 --- a/plugins/modules/vyos_firewall_global.py +++ b/plugins/modules/vyos_firewall_global.py @@ -1,1220 +1,1221 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_firewall_global """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_firewall_global short_description: FIREWALL global resource module description: This module manage global policies or configurations for firewall on VyOS devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: - A dictionary of Firewall global configuration options. type: dict suboptions: route_redirects: description: -A dictionary of Firewall icmp redirect and source route global configuration options. type: list elements: dict suboptions: afi: description: - Specifies IP address type type: str choices: - ipv4 - ipv6 required: true icmp_redirects: description: - Specifies whether to allow sending/receiving of IPv4/v6 ICMP redirect messages. type: dict suboptions: send: description: - Permits or denies transmitting packets ICMP redirect messages. type: bool receive: description: - Permits or denies receiving packets ICMP redirect messages. type: bool ip_src_route: description: - Specifies whether or not to process source route IP options. type: bool ping: description: - Policy for handling of all IPv4 ICMP echo requests. type: dict suboptions: all: description: - Enables or disables response to all IPv4 ICMP Echo Request (ping) messages. - The system responds to IPv4 ICMP Echo Request messages. type: bool broadcast: description: - Enables or disables response to broadcast IPv4 ICMP Echo Request and Timestamp Request messages. - IPv4 ICMP Echo and Timestamp Request messages are not processed. type: bool config_trap: description: - SNMP trap generation on firewall configuration changes. type: bool validation: description: - Specifies a policy for source validation by reversed path, as defined in RFC 3704. - (disable) No source validation is performed. - (loose) Enable Loose Reverse Path Forwarding as defined in RFC3704. - (strict) Enable Strict Reverse Path Forwarding as defined in RFC3704. type: str choices: - strict - loose - disable group: description: - Defines a group of objects for referencing in firewall rules. type: dict suboptions: address_group: description: - Defines a group of IP addresses for referencing in firewall rules. type: list elements: dict suboptions: afi: description: - Specifies IP address type type: str default: ipv4 choices: - ipv4 - ipv6 required: false name: description: - Name of the firewall address group. type: str required: true description: description: - Allows you to specify a brief description for the address group. type: str members: description: - Address-group members. - IPv4 address to match. - IPv4 range to match. type: list elements: dict suboptions: address: description: IP address. type: str network_group: description: - Defines a group of networks for referencing in firewall rules. type: list elements: dict suboptions: afi: description: - Specifies network address type type: str default: ipv4 choices: - ipv4 - ipv6 required: false name: description: - Name of the firewall network group. type: str required: true description: description: - Allows you to specify a brief description for the network group. type: str members: description: - Adds an IPv4 network to the specified network group. - The format is ip-address/prefix. type: list elements: dict suboptions: address: description: IP address. type: str port_group: description: - Defines a group of ports for referencing in firewall rules. type: list elements: dict suboptions: name: description: - Name of the firewall port group. type: str required: true description: description: - Allows you to specify a brief description for the port group. type: str members: description: - Port-group member. type: list elements: dict suboptions: port: description: Defines the number. type: str log_martians: description: - Specifies whether or not to record packets with invalid addresses in the log. - (True) Logs packets with invalid addresses. - (False) Does not log packets with invalid addresses. type: bool syn_cookies: description: - Specifies policy for using TCP SYN cookies with IPv4. - (True) Enables TCP SYN cookies with IPv4. - (False) Disables TCP SYN cookies with IPv4. type: bool twa_hazards_protection: description: - RFC1337 TCP TIME-WAIT assassination hazards protection. type: bool state_policy: description: - Specifies global firewall state-policy. type: list elements: dict suboptions: connection_type: description: Specifies connection type. type: str choices: - established - invalid - related action: description: - Action for packets part of an established connection. type: str choices: - accept - drop - reject log: description: - Enable logging of packets part of an established connection. type: bool running_config: description: - The module, by default, will connect to the remote device and retrieve the current running-config to use as a base for comparing against the contents of source. There are times when it is not desirable to have the task get the current running-config for every task in a playbook. The I(running_config) argument allows the implementer to pass in the configuration to use as the base config for comparison. This value of this option should be the output received from device by executing command C(show configuration commands | grep 'firewall') type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep firewall # # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_global: config: validation: strict config_trap: true log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: MGMT-HOSTS description: This group has the Management hosts address list members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set firewall group address-group MGMT-HOSTS address 192.0.1.1", # "set firewall group address-group MGMT-HOSTS address 192.0.1.3", # "set firewall group address-group MGMT-HOSTS address 192.0.1.5", # "set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'", # "set firewall group address-group MGMT-HOSTS", # "set firewall group network-group MGMT network 192.0.1.0/24", # "set firewall group network-group MGMT description 'This group has the Management network addresses'", # "set firewall group network-group MGMT", # "set firewall ip-src-route 'enable'", # "set firewall receive-redirects 'disable'", # "set firewall send-redirects 'enable'", # "set firewall config-trap 'enable'", # "set firewall state-policy established action 'accept'", # "set firewall state-policy established log 'enable'", # "set firewall state-policy invalid action 'reject'", # "set firewall broadcast-ping 'enable'", # "set firewall all-ping 'enable'", # "set firewall log-martians 'enable'", # "set firewall twa-hazards-protection 'enable'", # "set firewall syn-cookies 'enable'", # "set firewall source-validation 'strict'" # ] # # "after": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "This group has the Management hosts address list", # "members": [ # { # "address": "192.0.1.1" # }, # { # "address": "192.0.1.3" # }, # { # "address": "192.0.1.5" # } # ], # "name": "MGMT-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall all-ping 'enable' # set firewall broadcast-ping 'enable' # set firewall config-trap 'enable' # set firewall group address-group MGMT-HOSTS address '192.0.1.1' # set firewall group address-group MGMT-HOSTS address '192.0.1.3' # set firewall group address-group MGMT-HOSTS address '192.0.1.5' # set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # set firewall ip-src-route 'enable' # set firewall log-martians 'enable' # set firewall receive-redirects 'disable' # set firewall send-redirects 'enable' # set firewall source-validation 'strict' # set firewall state-policy established action 'accept' # set firewall state-policy established log 'enable' # set firewall state-policy invalid action 'reject' # set firewall syn-cookies 'enable' # set firewall twa-hazards-protection 'enable' # # # Using parsed # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_global: running_config: "set firewall all-ping 'enable' set firewall broadcast-ping 'enable' set firewall config-trap 'enable' set firewall group address-group ENG-HOSTS address '192.0.3.1' set firewall group address-group ENG-HOSTS address '192.0.3.2' set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' set firewall group address-group SALES-HOSTS address '192.0.2.1' set firewall group address-group SALES-HOSTS address '192.0.2.2' set firewall group address-group SALES-HOSTS address '192.0.2.3' set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' set firewall group network-group MGMT description 'This group has the Management network addresses' set firewall group network-group MGMT network '192.0.1.0/24' set firewall ip-src-route 'enable' set firewall log-martians 'enable' set firewall receive-redirects 'disable' set firewall send-redirects 'enable' set firewall source-validation 'strict' set firewall state-policy established action 'accept' set firewall state-policy established log 'enable' set firewall state-policy invalid action 'reject' set firewall syn-cookies 'enable' set firewall twa-hazards-protection 'enable'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # } # # # Using deleted # # Before state # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall all-ping 'enable' # set firewall broadcast-ping 'enable' # set firewall config-trap 'enable' # set firewall group address-group MGMT-HOSTS address '192.0.1.1' # set firewall group address-group MGMT-HOSTS address '192.0.1.3' # set firewall group address-group MGMT-HOSTS address '192.0.1.5' # set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # set firewall ip-src-route 'enable' # set firewall log-martians 'enable' # set firewall receive-redirects 'disable' # set firewall send-redirects 'enable' # set firewall source-validation 'strict' # set firewall state-policy established action 'accept' # set firewall state-policy established log 'enable' # set firewall state-policy invalid action 'reject' # set firewall syn-cookies 'enable' # set firewall twa-hazards-protection 'enable' - name: Delete attributes of firewall. vyos.vyos.vyos_firewall_global: config: state_policy: config_trap: log_martians: syn_cookies: twa_hazards_protection: route_redirects: ping: group: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "This group has the Management hosts address list", # "members": [ # { # "address": "192.0.1.1" # }, # { # "address": "192.0.1.3" # }, # { # "address": "192.0.1.5" # } # ], # "name": "MGMT-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # "commands": [ # "delete firewall source-validation", # "delete firewall group", # "delete firewall log-martians", # "delete firewall ip-src-route", # "delete firewall receive-redirects", # "delete firewall send-redirects", # "delete firewall config-trap", # "delete firewall state-policy", # "delete firewall syn-cookies", # "delete firewall broadcast-ping", # "delete firewall all-ping", # "delete firewall twa-hazards-protection" # ] # # "after": [] # After state # ------------ # vyos@192# run show configuration commands | grep firewall # set 'firewall' # # # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall all-ping 'enable' # set firewall broadcast-ping 'enable' # set firewall config-trap 'enable' # set firewall group address-group MGMT-HOSTS address '192.0.1.1' # set firewall group address-group MGMT-HOSTS address '192.0.1.3' # set firewall group address-group MGMT-HOSTS address '192.0.1.5' # set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # set firewall ip-src-route 'enable' # set firewall log-martians 'enable' # set firewall receive-redirects 'disable' # set firewall send-redirects 'enable' # set firewall source-validation 'strict' # set firewall state-policy established action 'accept' # set firewall state-policy established log 'enable' # set firewall state-policy invalid action 'reject' # set firewall syn-cookies 'enable' # set firewall twa-hazards-protection 'enable' # - name: Replace firewall global attributes configuration. vyos.vyos.vyos_firewall_global: config: validation: strict config_trap: true log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: SALES-HOSTS description: Sales office hosts address list members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 - name: ENG-HOSTS description: Sales office hosts address list members: - address: 192.0.3.1 - address: 192.0.3.2 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "This group has the Management hosts address list", # "members": [ # { # "address": "192.0.1.1" # }, # { # "address": "192.0.1.3" # }, # { # "address": "192.0.1.5" # } # ], # "name": "MGMT-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # "commands": [ # "delete firewall group address-group MGMT-HOSTS", # "set firewall group address-group SALES-HOSTS address 192.0.2.1", # "set firewall group address-group SALES-HOSTS address 192.0.2.2", # "set firewall group address-group SALES-HOSTS address 192.0.2.3", # "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group SALES-HOSTS", # "set firewall group address-group ENG-HOSTS address 192.0.3.1", # "set firewall group address-group ENG-HOSTS address 192.0.3.2", # "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group ENG-HOSTS" # ] # # "after": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall all-ping 'enable' # set firewall broadcast-ping 'enable' # set firewall config-trap 'enable' # set firewall group address-group ENG-HOSTS address '192.0.3.1' # set firewall group address-group ENG-HOSTS address '192.0.3.2' # set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' # set firewall group address-group SALES-HOSTS address '192.0.2.1' # set firewall group address-group SALES-HOSTS address '192.0.2.2' # set firewall group address-group SALES-HOSTS address '192.0.2.3' # set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # set firewall ip-src-route 'enable' # set firewall log-martians 'enable' # set firewall receive-redirects 'disable' # set firewall send-redirects 'enable' # set firewall source-validation 'strict' # set firewall state-policy established action 'accept' # set firewall state-policy established log 'enable' # set firewall state-policy invalid action 'reject' # set firewall syn-cookies 'enable' # set firewall twa-hazards-protection 'enable' # # # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall all-ping 'enable' # set firewall broadcast-ping 'enable' # set firewall config-trap 'enable' # set firewall group address-group ENG-HOSTS address '192.0.3.1' # set firewall group address-group ENG-HOSTS address '192.0.3.2' # set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' # set firewall group address-group SALES-HOSTS address '192.0.2.1' # set firewall group address-group SALES-HOSTS address '192.0.2.2' # set firewall group address-group SALES-HOSTS address '192.0.2.3' # set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # set firewall ip-src-route 'enable' # set firewall log-martians 'enable' # set firewall receive-redirects 'disable' # set firewall send-redirects 'enable' # set firewall source-validation 'strict' # set firewall state-policy established action 'accept' # set firewall state-policy established log 'enable' # set firewall state-policy invalid action 'reject' # set firewall syn-cookies 'enable' # set firewall twa-hazards-protection 'enable' # - name: Gather firewall global config with provided configurations vyos.vyos.vyos_firewall_global: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall all-ping 'enable' # set firewall broadcast-ping 'enable' # set firewall config-trap 'enable' # set firewall group address-group ENG-HOSTS address '192.0.3.1' # set firewall group address-group ENG-HOSTS address '192.0.3.2' # set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' # set firewall group address-group SALES-HOSTS address '192.0.2.1' # set firewall group address-group SALES-HOSTS address '192.0.2.2' # set firewall group address-group SALES-HOSTS address '192.0.2.3' # set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' # set firewall group network-group MGMT description 'This group has the Management network addresses' # set firewall group network-group MGMT network '192.0.1.0/24' # set firewall ip-src-route 'enable' # set firewall log-martians 'enable' # set firewall receive-redirects 'disable' # set firewall send-redirects 'enable' # set firewall source-validation 'strict' # set firewall state-policy established action 'accept' # set firewall state-policy established log 'enable' # set firewall state-policy invalid action 'reject' # set firewall syn-cookies 'enable' # set firewall twa-hazards-protection 'enable' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_global: config: validation: strict config_trap: true log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: SALES-HOSTS description: Sales office hosts address list members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 - name: ENG-HOSTS description: Sales office hosts address list members: - address: 192.0.3.1 - address: 192.0.3.2 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set firewall group address-group SALES-HOSTS address 192.0.2.1", # "set firewall group address-group SALES-HOSTS address 192.0.2.2", # "set firewall group address-group SALES-HOSTS address 192.0.2.3", # "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group SALES-HOSTS", # "set firewall group address-group ENG-HOSTS address 192.0.3.1", # "set firewall group address-group ENG-HOSTS address 192.0.3.2", # "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'", # "set firewall group address-group ENG-HOSTS", # "set firewall group network-group MGMT network 192.0.1.0/24", # "set firewall group network-group MGMT description 'This group has the Management network addresses'", # "set firewall group network-group MGMT", # "set firewall ip-src-route 'enable'", # "set firewall receive-redirects 'disable'", # "set firewall send-redirects 'enable'", # "set firewall config-trap 'enable'", # "set firewall state-policy established action 'accept'", # "set firewall state-policy established log 'enable'", # "set firewall state-policy invalid action 'reject'", # "set firewall broadcast-ping 'enable'", # "set firewall all-ping 'enable'", # "set firewall log-martians 'enable'", # "set firewall twa-hazards-protection 'enable'", # "set firewall syn-cookies 'enable'", # "set firewall source-validation 'strict'" # ] # # """ RETURN = """ before: description: The configuration prior to the model invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: ['set firewall group address-group ENG-HOSTS', 'set firewall group address-group ENG-HOSTS address 192.0.3.1'] """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_global.firewall_global import ( Firewall_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_global.firewall_global import ( Firewall_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_globalArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_interfaces.py b/plugins/modules/vyos_firewall_interfaces.py index 7ca936d..32ad09f 100644 --- a/plugins/modules/vyos_firewall_interfaces.py +++ b/plugins/modules/vyos_firewall_interfaces.py @@ -1,1288 +1,1289 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_firewall_interfaces """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_firewall_interfaces short_description: FIREWALL interfaces resource module description: Manage firewall rules of interfaces on VyOS network devices. version_added: 1.0.0 author: - Rohit Thakur (@rohitthakur2590) options: config: description: A list of firewall rules options for interfaces. type: list elements: dict suboptions: name: description: - Name/Identifier for the interface. type: str required: true access_rules: description: - Specifies firewall rules attached to the interfaces. type: list elements: dict suboptions: afi: description: - Specifies the AFI for the Firewall rules to be configured on this interface. type: str choices: - ipv4 - ipv6 required: true rules: description: - Specifies the firewall rules for the provided AFI. type: list elements: dict suboptions: name: description: - Specifies the name of the IPv4/IPv6 Firewall rule for the interface. type: str direction: description: - Specifies the direction of packets that the firewall rule will be applied on. type: str choices: - in - local - out required: true running_config: description: - The module, by default, will connect to the remote device and retrieve the current running-config to use as a base for comparing against the contents of source. There are times when it is not desirable to have the task get the current running-config for every task in a playbook. The I(running_config) argument allows the implementer to pass in the configuration to use as the base config for comparison. This value of this option should be the output received from device by executing command C(show configuration commands | grep 'firewall' type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - overridden - deleted - parsed - rendered - gathered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@192# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_interfaces: config: - access_rules: - afi: ipv4 rules: - name: INBOUND direction: in - name: OUTBOUND direction: out - name: LOCAL direction: local - afi: ipv6 rules: - name: V6-LOCAL direction: local name: eth1 - access_rules: - afi: ipv4 rules: - name: INBOUND direction: in - name: OUTBOUND direction: out - name: LOCAL direction: local - afi: ipv6 rules: - name: V6-LOCAL direction: local name: eth3 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ] # # "commands": [ # "set interfaces ethernet eth1 firewall in name 'INBOUND'", # "set interfaces ethernet eth1 firewall out name 'OUTBOUND'", # "set interfaces ethernet eth1 firewall local name 'LOCAL'", # "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'", # "set interfaces ethernet eth3 firewall in name 'INBOUND'", # "set interfaces ethernet eth3 firewall out name 'OUTBOUND'", # "set interfaces ethernet eth3 firewall local name 'LOCAL'", # "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'" # ] # # "after": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_interfaces: config: - access_rules: - afi: ipv4 rules: - name: OUTBOUND direction: in - name: INBOUND direction: out name: eth1 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # # "commands": [ # "set interfaces ethernet eth1 firewall in name 'OUTBOUND'", # "set interfaces ethernet eth1 firewall out name 'INBOUND'" # ] # # "after": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "OUTBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "INBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'OUTBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'INBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Replace device configurations of listed firewall interfaces with provided configurations vyos.vyos.vyos_firewall_interfaces: config: - name: eth1 access_rules: - afi: ipv4 rules: - name: OUTBOUND direction: out - afi: ipv6 rules: - name: V6-LOCAL direction: local - name: eth3 access_rules: - afi: ipv4 rules: - name: INBOUND direction: in state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # # "commands": [ # "delete interfaces ethernet eth1 firewall in name", # "delete interfaces ethernet eth1 firewall local name", # "delete interfaces ethernet eth3 firewall local name", # "delete interfaces ethernet eth3 firewall out name", # "delete interfaces ethernet eth3 firewall local ipv6-name" # ] # # "after": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_firewall_interfaces: config: - name: eth3 access_rules: - afi: ipv4 rules: - name: INBOUND direction: out state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before":[ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # "commands": [ # "delete interfaces ethernet eth1 firewall", # "delete interfaces ethernet eth3 firewall in name", # "set interfaces ethernet eth3 firewall out name 'INBOUND'" # # # "after": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # # After state # ------------ # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth3 firewall 'in' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall out name 'INBOUND' # Using deleted per interface name # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Delete firewall interfaces based on interface name. vyos.vyos.vyos_firewall_interfaces: config: - name: eth1 - name: eth3 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth3" # } # ] # "commands": [ # "delete interfaces ethernet eth1 firewall", # "delete interfaces ethernet eth3 firewall" # ] # # "after": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # Using deleted per afi # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Delete firewall interfaces config per afi. vyos.vyos.vyos_firewall_interfaces: config: - name: eth1 access_rules: - afi: ipv4 - afi: ipv6 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "commands": [ # "delete interfaces ethernet eth1 firewall in name", # "delete interfaces ethernet eth1 firewall out name", # "delete interfaces ethernet eth1 firewall local name", # "delete interfaces ethernet eth1 firewall local ipv6-name" # ] # # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # Using deleted without config # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall in name 'INBOUND' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall local name 'LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth3 firewall local name 'LOCAL' # set interfaces ethernet eth3 firewall out name 'OUTBOUND' # - name: Delete firewall interfaces config when empty config provided. vyos.vyos.vyos_firewall_interfaces: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "commands": [ # "delete interfaces ethernet eth1 firewall", # "delete interfaces ethernet eth1 firewall" # ] # # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # Using parsed # # - name: Parse the provided configuration vyos.vyos.vyos_firewall_interfaces: running_config: "set interfaces ethernet eth1 firewall in name 'INBOUND' set interfaces ethernet eth1 firewall out name 'OUTBOUND' set interfaces ethernet eth1 firewall local name 'LOCAL' set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' set interfaces ethernet eth2 firewall in name 'INBOUND' set interfaces ethernet eth2 firewall out name 'OUTBOUND' set interfaces ethernet eth2 firewall local name 'LOCAL' set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # }, # { # "direction": "local", # "name": "LOCAL" # }, # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth2" # }, # { # "name": "eth3" # } # ] # Using gathered # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # - name: Gather listed firewall interfaces. vyos.vyos.vyos_firewall_interfaces: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "name": "eth0" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "out", # "name": "OUTBOUND" # } # ] # }, # { # "afi": "ipv6", # "rules": [ # { # "direction": "local", # "name": "V6-LOCAL" # } # ] # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "access_rules": [ # { # "afi": "ipv4", # "rules": [ # { # "direction": "in", # "name": "INBOUND" # } # ] # } # ], # "name": "eth3" # } # ] # # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name 'V6-LOCAL' # set firewall name 'INBOUND' # set firewall name 'LOCAL' # set firewall name 'OUTBOUND' # set interfaces ethernet eth1 firewall 'in' # set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL' # set interfaces ethernet eth1 firewall out name 'OUTBOUND' # set interfaces ethernet eth3 firewall in name 'INBOUND' # set interfaces ethernet eth3 firewall 'local' # set interfaces ethernet eth3 firewall 'out' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_interfaces: config: - name: eth2 access_rules: - afi: ipv4 rules: - direction: in name: INGRESS - direction: out name: OUTGRESS - direction: local name: DROP state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces ethernet eth2 firewall in name 'INGRESS'", # "set interfaces ethernet eth2 firewall out name 'OUTGRESS'", # "set interfaces ethernet eth2 firewall local name 'DROP'", # "set interfaces ethernet eth2 firewall local ipv6-name 'LOCAL'" # ] """ RETURN = """ before: description: The configuration prior to the model invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'" - "set interfaces ethernet eth3 firewall in name 'INBOUND'" """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import ( Firewall_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_interfaces.firewall_interfaces import ( Firewall_interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py index 9730c4a..c219f54 100644 --- a/plugins/modules/vyos_firewall_rules.py +++ b/plugins/modules/vyos_firewall_rules.py @@ -1,1522 +1,1523 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_firewall_rules """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_firewall_rules short_description: FIREWALL rules resource module description: This module manages firewall rule-set attributes on VyOS devices version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A dictionary of Firewall rule-set options. type: list elements: dict suboptions: afi: description: - Specifies the type of rule-set. type: str choices: - ipv4 - ipv6 required: true rule_sets: description: - The Firewall rule-set list. type: list elements: dict suboptions: name: description: - Firewall rule set name. type: str default_action: description: - Default action for rule-set. - drop (Drop if no prior rules are hit (default)) - reject (Drop and notify source if no prior rules are hit) - accept (Accept if no prior rules are hit) type: str choices: - drop - reject - accept description: description: - Rule set description. type: str enable_default_log: description: - Option to log packets hitting default-action. type: bool rules: description: - A dictionary that specifies the rule-set configurations. type: list elements: dict suboptions: number: description: - Rule number. type: int required: true description: description: - Description of this rule. type: str action: description: - Specifying the action. type: str choices: - drop - reject - accept - inspect destination: description: - Specifying the destination parameters. type: dict suboptions: address: description: - Destination ip address subnet or range. - IPv4/6 address, subnet or range to match. - Match everything except the specified address, subnet or range. - Destination ip address subnet or range. type: str group: description: - Destination group. type: dict suboptions: address_group: description: - Group of addresses. type: str network_group: description: - Group of networks. type: str port_group: description: - Group of ports. type: str port: description: - Multiple destination ports can be specified as a comma-separated list. - The whole list can also be "negated" using '!'. - For example:'!22,telnet,http,123,1001-1005'. type: str disable: description: - Option to disable firewall rule. type: bool aliases: ["disabled"] fragment: description: - IP fragment match. type: str choices: - match-frag - match-non-frag icmp: description: - ICMP type and code information. type: dict suboptions: type_name: description: - ICMP type-name. type: str choices: - any - echo-reply - destination-unreachable - network-unreachable - host-unreachable - protocol-unreachable - port-unreachable - fragmentation-needed - source-route-failed - network-unknown - host-unknown - network-prohibited - host-prohibited - TOS-network-unreachable - TOS-host-unreachable - communication-prohibited - host-precedence-violation - precedence-cutoff - source-quench - redirect - network-redirect - host-redirect - TOS-network-redirect - TOS-host-redirect - echo-request - router-advertisement - router-solicitation - time-exceeded - ttl-zero-during-transit - ttl-zero-during-reassembly - parameter-problem - ip-header-bad - required-option-missing - timestamp-request - timestamp-reply - address-mask-request - address-mask-reply - ping - pong - ttl-exceeded code: description: - ICMP code. type: int type: description: - ICMP type. type: int ipsec: description: - Inbound ip sec packets. type: str choices: - match-ipsec - match-none log: description: - Option to log packets matching rule type: str choices: - disable - enable limit: description: - Rate limit using a token bucket filter. type: dict suboptions: burst: description: - Maximum number of packets to allow in excess of rate. type: int rate: description: - format for rate (integer/time unit). - any one of second, minute, hour or day may be used to specify time unit. - eg. 1/second implies rule to be matched at an average of once per second. type: dict suboptions: number: description: - This is the integer value. type: int unit: description: - This is the time unit. type: str p2p: description: - P2P application packets. type: list elements: dict suboptions: application: description: - Name of the application. type: str choices: - all - applejuice - bittorrent - directconnect - edonkey - gnutella - kazaa protocol: description: - Protocol to match (protocol name in /etc/protocols or protocol number or all). - IP protocol name from /etc/protocols (e.g. "tcp" or "udp"). - <0-255> IP protocol number. - tcp_udp Both TCP and UDP. - all All IP protocols. - (!)All IP protocols except for the specified name or number. type: str recent: description: - Parameters for matching recently seen sources. type: dict suboptions: count: description: - Source addresses seen more than N times. type: int time: description: - Source addresses seen in the last N seconds. type: int source: description: - Source parameters. type: dict suboptions: address: description: - Source ip address subnet or range. - IPv4/6 address, subnet or range to match. - Match everything except the specified address, subnet or range. - Source ip address subnet or range. type: str group: description: - Source group. type: dict suboptions: address_group: description: - Group of addresses. type: str network_group: description: - Group of networks. type: str port_group: description: - Group of ports. type: str port: description: - Multiple source ports can be specified as a comma-separated list. - The whole list can also be "negated" using '!'. - For example:'!22,telnet,http,123,1001-1005'. type: str mac_address: description: - MAC address to match. - Match everything except the specified MAC address. type: str state: description: - Session state. type: dict suboptions: established: description: - Established state. type: bool invalid: description: - Invalid state. type: bool new: description: - New state. type: bool related: description: - Related state. type: bool tcp: description: - TCP flags to match. type: dict suboptions: flags: description: - TCP flags to be matched. type: str time: description: - Time to match rule. type: dict suboptions: utc: description: - Interpret times for startdate, stopdate, starttime and stoptime to be UTC. type: bool monthdays: description: - Monthdays to match rule on. type: str startdate: description: - Date to start matching rule. type: str starttime: description: - Time of day to start matching rule. type: str stopdate: description: - Date to stop matching rule. type: str stoptime: description: - Time of day to stop matching rule. type: str weekdays: description: - Weekdays to match rule on. type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep firewall). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in type: str choices: - merged - replaced - overridden - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using deleted to delete firewall rules based on rule-set name # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall name Downlink default-action 'accept' # set firewall name Downlink description 'IPv4 INBOUND rule set' # set firewall name Downlink rule 501 action 'accept' # set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall name Downlink rule 501 ipsec 'match-ipsec' # set firewall name Downlink rule 502 action 'reject' # set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall name Downlink rule 502 ipsec 'match-ipsec' # - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall name Downlink" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using deleted to delete firewall rules based on afi # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6-name UPLINK rule 1 action 'accept' # set firewall ipv6-name UPLINK rule 1 # set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6-name UPLINK rule 2 action 'accept' # set firewall ipv6-name UPLINK rule 2 # set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' # set firewall group address-group 'inbound' # set firewall name Downlink default-action 'accept' # set firewall name Downlink description 'IPv4 INBOUND rule set' # set firewall name Downlink rule 501 action 'accept' # set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall name Downlink rule 501 ipsec 'match-ipsec' # set firewall name Downlink rule 502 action 'reject' # set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall name Downlink rule 502 ipsec 'match-ipsec' # - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall name" # ] # # "after": [] # After state # ------------ # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6-name UPLINK rule 1 action 'accept' # set firewall ipv6-name UPLINK rule 1 # set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6-name UPLINK rule 2 action 'accept' # set firewall ipv6-name UPLINK rule 2 # set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' # Using deleted to delete all the the firewall rules when provided config is empty # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall name Downlink default-action 'accept' # set firewall name Downlink description 'IPv4 INBOUND rule set' # set firewall name Downlink rule 501 action 'accept' # set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall name Downlink rule 501 ipsec 'match-ipsec' # set firewall name Downlink rule 502 action 'reject' # set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall name Downlink rule 502 ipsec 'match-ipsec' # - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall name" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept rules: - number: 1 action: accept description: Fwipv6-Rule 1 is configured by Ansible ipsec: match-ipsec - number: 2 action: accept description: Fwipv6-Rule 2 is configured by Ansible ipsec: match-ipsec - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set firewall ipv6-name UPLINK default-action 'accept'", # "set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv6-name UPLINK rule 1 action 'accept'", # "set firewall ipv6-name UPLINK rule 1", # "set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'", # "set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec'", # "set firewall ipv6-name UPLINK rule 2 action 'accept'", # "set firewall ipv6-name UPLINK rule 2", # "set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'", # "set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec'", # "set firewall name INBOUND default-action 'accept'", # "set firewall name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall name INBOUND rule 101 action 'accept'", # "set firewall name INBOUND rule 101", # "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall name INBOUND rule 102 action 'reject'", # "set firewall name INBOUND rule 102", # "set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall name INBOUND rule 103 destination group address-group inbound", # "set firewall name INBOUND rule 103", # "set firewall name INBOUND rule 103 source address 192.0.2.0", # "set firewall name INBOUND rule 103 state established enable", # "set firewall name INBOUND rule 103 state related enable", # "set firewall name INBOUND rule 103 state invalid disable", # "set firewall name INBOUND rule 103 state new disable", # "set firewall name INBOUND rule 103 action 'accept'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6-name UPLINK rule 1 action 'accept' # set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6-name UPLINK rule 2 action 'accept' # set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' # set firewall name INBOUND default-action 'accept' # set firewall name INBOUND description 'IPv4 INBOUND rule set' # set firewall name INBOUND rule 101 action 'accept' # set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall name INBOUND rule 101 ipsec 'match-ipsec' # set firewall name INBOUND rule 102 action 'reject' # set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall name INBOUND rule 102 ipsec 'match-ipsec' # set firewall name INBOUND rule 103 action 'accept' # set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall name INBOUND rule 103 destination group address-group 'inbound' # set firewall name INBOUND rule 103 source address '192.0.2.0' # set firewall name INBOUND rule 103 state established 'enable' # set firewall name INBOUND rule 103 state invalid 'disable' # set firewall name INBOUND rule 103 state new 'disable' # set firewall name INBOUND rule 103 state related 'enable' # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6-name UPLINK rule 1 action 'accept' # set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6-name UPLINK rule 2 action 'accept' # set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' # set firewall name INBOUND default-action 'accept' # set firewall name INBOUND description 'IPv4 INBOUND rule set' # set firewall name INBOUND rule 101 action 'accept' # set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall name INBOUND rule 101 ipsec 'match-ipsec' # set firewall name INBOUND rule 102 action 'reject' # set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall name INBOUND rule 102 ipsec 'match-ipsec' # set firewall name INBOUND rule 103 action 'accept' # set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall name INBOUND rule 103 destination group address-group 'inbound' # set firewall name INBOUND rule 103 source address '192.0.2.0' # set firewall name INBOUND rule 103 state established 'enable' # set firewall name INBOUND rule 103 state invalid 'disable' # set firewall name INBOUND rule 103 state new 'disable' # set firewall name INBOUND rule 103 state related 'enable' # - name: Replace device configurations of listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 104 action: reject description: Rule 104 is configured by Ansible ipsec: match-none state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6-name UPLINK rule 1", # "delete firewall ipv6-name UPLINK rule 2", # "delete firewall name INBOUND rule 102", # "delete firewall name INBOUND rule 103", # "set firewall name INBOUND rule 104 action 'reject'", # "set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible'", # "set firewall name INBOUND rule 104", # "set firewall name INBOUND rule 104 ipsec 'match-none'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall name INBOUND default-action 'accept' # set firewall name INBOUND description 'IPv4 INBOUND rule set' # set firewall name INBOUND rule 101 action 'accept' # set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall name INBOUND rule 101 ipsec 'match-ipsec' # set firewall name INBOUND rule 104 action 'reject' # set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall name INBOUND rule 104 ipsec 'match-none' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall name INBOUND default-action 'accept' # set firewall name INBOUND description 'IPv4 INBOUND rule set' # set firewall name INBOUND rule 101 action 'accept' # set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall name INBOUND rule 101 ipsec 'match-ipsec' # set firewall name INBOUND rule 104 action 'reject' # set firewall name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall name INBOUND rule 104 ipsec 'match-none' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink description: IPv4 INBOUND rule set default_action: accept rules: - number: 501 action: accept description: Rule 501 is configured by Ansible ipsec: match-ipsec - number: 502 action: reject description: Rule 502 is configured by Ansible ipsec: match-ipsec state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6-name UPLINK", # "delete firewall name INBOUND", # "set firewall name Downlink default-action 'accept'", # "set firewall name Downlink description 'IPv4 INBOUND rule set'", # "set firewall name Downlink rule 501 action 'accept'", # "set firewall name Downlink rule 501", # "set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible'", # "set firewall name Downlink rule 501 ipsec 'match-ipsec'", # "set firewall name Downlink rule 502 action 'reject'", # "set firewall name Downlink rule 502", # "set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'", # "set firewall name Downlink rule 502 ipsec 'match-ipsec'" # # # "after": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # # # After state # ------------ # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall name Downlink default-action 'accept' # set firewall name Downlink description 'IPv4 INBOUND rule set' # set firewall name Downlink rule 501 action 'accept' # set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall name Downlink rule 501 ipsec 'match-ipsec' # set firewall name Downlink rule 502 action 'reject' # set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall name Downlink rule 502 ipsec 'match-ipsec' # Using gathered # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6-name UPLINK rule 1 action 'accept' # set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6-name UPLINK rule 2 action 'accept' # set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' # set firewall name INBOUND default-action 'accept' # set firewall name INBOUND description 'IPv4 INBOUND rule set' # set firewall name INBOUND rule 101 action 'accept' # set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall name INBOUND rule 101 ipsec 'match-ipsec' # set firewall name INBOUND rule 102 action 'reject' # set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall name INBOUND rule 102 ipsec 'match-ipsec' # set firewall name INBOUND rule 103 action 'accept' # set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall name INBOUND rule 103 destination group address-group 'inbound' # set firewall name INBOUND rule 103 source address '192.0.2.0' # set firewall name INBOUND rule 103 state established 'enable' # set firewall name INBOUND rule 103 state invalid 'disable' # set firewall name INBOUND rule 103 state new 'disable' # set firewall name INBOUND rule 103 state related 'enable' # - name: Gather listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6-name UPLINK default-action 'accept' # set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6-name UPLINK rule 1 action 'accept' # set firewall ipv6-name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6-name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6-name UPLINK rule 2 action 'accept' # set firewall ipv6-name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6-name UPLINK rule 2 ipsec 'match-ipsec' # set firewall name INBOUND default-action 'accept' # set firewall name INBOUND description 'IPv4 INBOUND rule set' # set firewall name INBOUND rule 101 action 'accept' # set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall name INBOUND rule 101 ipsec 'match-ipsec' # set firewall name INBOUND rule 102 action 'reject' # set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall name INBOUND rule 102 ipsec 'match-ipsec' # set firewall name INBOUND rule 103 action 'accept' # set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall name INBOUND rule 103 destination group address-group 'inbound' # set firewall name INBOUND rule 103 source address '192.0.2.0' # set firewall name INBOUND rule 103 state established 'enable' # set firewall name INBOUND rule 103 state invalid 'disable' # set firewall name INBOUND rule 103 state new 'disable' # set firewall name INBOUND rule 103 state related 'enable' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set firewall ipv6-name UPLINK default-action 'accept'", # "set firewall ipv6-name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall name INBOUND default-action 'accept'", # "set firewall name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall name INBOUND rule 101 action 'accept'", # "set firewall name INBOUND rule 101", # "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall name INBOUND rule 102 action 'reject'", # "set firewall name INBOUND rule 102", # "set firewall name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall name INBOUND rule 103 destination group address-group inbound", # "set firewall name INBOUND rule 103", # "set firewall name INBOUND rule 103 source address 192.0.2.0", # "set firewall name INBOUND rule 103 state established enable", # "set firewall name INBOUND rule 103 state related enable", # "set firewall name INBOUND rule 103 state invalid disable", # "set firewall name INBOUND rule 103 state new disable", # "set firewall name INBOUND rule 103 action 'accept'" # ] # Using parsed # # - name: Parsed the provided input commands. vyos.vyos.vyos_firewall_rules: running_config: "set firewall group address-group 'inbound' set firewall name Downlink default-action 'accept' set firewall name Downlink description 'IPv4 INBOUND rule set' set firewall name Downlink rule 501 action 'accept' set firewall name Downlink rule 501 description 'Rule 501 is configured by Ansible' set firewall name Downlink rule 501 ipsec 'match-ipsec' set firewall name Downlink rule 502 action 'reject' set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible' set firewall name Downlink rule 502 ipsec 'match-ipsec'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] """ RETURN = """ before: description: The configuration prior to the model invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set firewall name Downlink default-action 'accept'" - "set firewall name Downlink description 'IPv4 INBOUND rule set'" - "set firewall name Downlink rule 501 action 'accept'" - "set firewall name Downlink rule 502 description 'Rule 502 is configured by Ansible'" - "set firewall name Downlink rule 502 ipsec 'match-ipsec'" """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( Firewall_rulesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import ( Firewall_rules, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_rulesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_rules(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_hostname.py b/plugins/modules/vyos_hostname.py index da22f41..46b692a 100644 --- a/plugins/modules/vyos_hostname.py +++ b/plugins/modules/vyos_hostname.py @@ -1,284 +1,285 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2022 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_hostname """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_hostname version_added: 2.8.0 short_description: Manages hostname resource module description: This module manages the hostname attribute of Vyos network devices author: Gomathi Selvi Srinivasan (@GomathiselviS) notes: - Tested against vyos 1.1.8 - This module works with connection C(network_cli). - The Configuration defaults of the Vyos network devices are supposed to hinder idempotent behavior of plays options: config: description: Hostname configuration. type: dict suboptions: hostname: description: set hostname for VYOS. type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the vyos device by executing the command B("show configuration commands | grep host-name"). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: choices: - merged - replaced - overridden - deleted - gathered - parsed - rendered default: merged description: - The state the configuration should be left in - The states I(rendered), I(gathered) and I(parsed) does not perform any change on the device. - The state I(rendered) will transform the configuration in C(config) option to platform specific CLI commands which will be returned in the I(rendered) key within the result. For state I(rendered) active connection to remote host is not required. - The states I(merged), I(replaced) and I(overridden) have identical behaviour for this module. - The state I(gathered) will fetch the running configuration from device and transform it into structured data in the format as per the resource module argspec and the value is returned in the I(gathered) key within the result. - The state I(parsed) reads the configuration from C(running_config) option and transforms it into JSON format as per the resource module parameters and the value is returned in the I(parsed) key within the result. The value of C(running_config) option should be the same format as the output of command I(show configuration commands | grep host-name) executed on device. For state I(parsed) active connection to remote host is not required. type: str """ EXAMPLES = """ # Using state: merged # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyostest' # Merged play: # ------------ - name: Apply the provided configuration vyos.vyos.vyos_hostname: config: hostname: vyos state: merged # Commands Fired: # --------------- # "commands": [ # "hostname vyos", # ], # After state: # ------------ # test#show configuration commands | grep host-name # set system host-name 'vyos' # Using state: deleted # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyos' # Deleted play: # ------------- - name: Remove all existing configuration vyos.vyos.vyos_hostname: state: deleted # Commands Fired: # --------------- # "commands": [ # "no hostname vyosTest", # ], # After state: # ------------ # test#show configuration commands | grep host-name # Using state: overridden # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyos' # Overridden play: # ---------------- - name: Override commands with provided configuration vyos.vyos.vyos_hostname: config: hostname: vyosTest state: overridden # Commands Fired: # --------------- # "commands": [ # "hostname vyosTest", # ], # After state: # ------------ # test#show configuration commands | grep host-name # set system host-name 'vyosTest' # Using state: replaced # Before state: # ------------- # test#show configuration commands | grep host-name # set system host-name 'vyosTest' # Replaced play: # -------------- - name: Replace commands with provided configuration vyos.vyos.vyos_hostname: config: hostname: vyos state: replaced # After state: # ------------ # test#show configuration commands | grep host-name # set system host-name 'vyos' # Using state: gathered # Before state: # ------------- #test#show configuration commands | grep host-name # set system host-name 'vyos' # Gathered play: # -------------- - name: Gather listed hostname config vyos.vyos.vyos_hostname: state: gathered # Module Execution Result: # ------------------------ # "gathered": { # "hostname": "vyos" # }, # Using state: rendered # Rendered play: # -------------- - name: Render the commands for provided configuration vyos.vyos.vyos_hostname: config: hostname: vyosTest state: rendered # Module Execution Result: # ------------------------ # "rendered": [ # "set system host-name vyosTest", # ] # Using state: parsed # File: parsed.cfg # ---------------- # set system host-name 'vyos' # Parsed play: # ------------ - name: Parse the provided configuration with the existing running configuration vyos.vyos.vyos_hostname: running_config: "{{ lookup('file', 'parsed.cfg') }}" state: parsed # Module Execution Result: # ------------------------ # "parsed": { # "hostname": "vyos" # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - sample command 1 - sample command 2 - sample command 3 rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - sample command 1 - sample command 2 - sample command 3 gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.hostname.hostname import ( HostnameArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.hostname.hostname import ( Hostname, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=HostnameArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Hostname(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_interfaces.py b/plugins/modules/vyos_interfaces.py index 4429e4b..47ff604 100644 --- a/plugins/modules/vyos_interfaces.py +++ b/plugins/modules/vyos_interfaces.py @@ -1,1134 +1,1135 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_interfaces """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_interfaces short_description: Interfaces resource module description: - This module manages the interface attributes on VyOS network devices. - This module supports managing base attributes of Ethernet, Bonding, VXLAN, Loopback and Virtual Tunnel Interfaces. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Nilashish Chakraborty (@nilashishc) - Rohit Thakur (@rohitthakur2590) options: config: description: The provided interfaces configuration. type: list elements: dict suboptions: name: description: - Full name of the interface, e.g. eth0, eth1, bond0, vti1, vxlan2. type: str required: true description: description: - Interface description. type: str duplex: description: - Interface duplex mode. - Applicable for Ethernet interfaces only. choices: - full - half - auto type: str enabled: default: true description: - Administrative state of the interface. - Set the value to C(true) to administratively enable the interface or C(false) to disable it. type: bool mtu: description: - MTU for a specific interface. Refer to vendor documentation for valid values. - Applicable for Ethernet, Bonding, VXLAN and Virtual Tunnel interfaces. type: int speed: description: - Interface link speed. - Applicable for Ethernet interfaces only. type: str choices: - auto - '10' - '100' - '1000' - '2500' - '10000' vifs: description: - Virtual sub-interfaces related configuration. - 802.1Q VLAN interfaces are represented as virtual sub-interfaces in VyOS. type: list elements: dict suboptions: vlan_id: description: - Identifier for the virtual sub-interface. type: int description: description: - Virtual sub-interface description. type: str enabled: description: - Administrative state of the virtual sub-interface. - Set the value to C(true) to administratively enable the interface or C(false) to disable it. type: bool default: true mtu: description: - MTU for the virtual sub-interface. - Refer to vendor documentation for valid values. type: int running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep interfaces). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - rendered - gathered - parsed default: merged """ EXAMPLES = """ # Using merged # # ------------- # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo - name: Merge provided configuration with device configuration vyos.vyos.vyos_interfaces: config: - name: eth2 description: Configured by Ansible enabled: true vifs: - vlan_id: 200 description: VIF 200 - ETH2 - name: eth3 description: Configured by Ansible mtu: 1500 - name: bond1 description: Bond - 1 mtu: 1200 - name: vti2 description: VTI - 2 enabled: false state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "enabled": true, # "name": "lo" # }, # { # "enabled": true, # "name": "eth3" # }, # { # "enabled": true, # "name": "eth2" # }, # { # "enabled": true, # "name": "eth1" # }, # { # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # "commands": [ # "set interfaces ethernet eth2 description 'Configured by Ansible'", # "set interfaces ethernet eth2 vif 200", # "set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'", # "set interfaces ethernet eth3 description 'Configured by Ansible'", # "set interfaces ethernet eth3 mtu '1500'", # "set interfaces bonding bond1", # "set interfaces bonding bond1 description 'Bond - 1'", # "set interfaces bonding bond1 mtu '1200'", # "set interfaces vti vti2", # "set interfaces vti vti2 description 'VTI - 2'", # "set interfaces vti vti2 disable" # ] # # "after": [ # { # "description": "Bond - 1", # "enabled": true, # "mtu": 1200, # "name": "bond1" # }, # { # "enabled": true, # "name": "lo" # }, # { # "description": "VTI - 2", # "enabled": false, # "name": "vti2" # }, # { # "description": "Configured by Ansible", # "enabled": true, # "mtu": 1500, # "name": "eth3" # }, # { # "description": "Configured by Ansible", # "enabled": true, # "name": "eth2", # "vifs": [ # { # "description": "VIF 200 - ETH2", # "enabled": true, # "vlan_id": "200" # } # ] # }, # { # "enabled": true, # "name": "eth1" # }, # { # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # # ------------- # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1200' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2' # set interfaces ethernet eth3 description 'Configured by Ansible' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 mtu '1500' # set interfaces loopback lo # set interfaces vti vti2 description 'VTI - 2' # set interfaces vti vti2 disable # # Using replaced # # ------------- # Before state: # ------------- # # vyos:~$ show configuration commands | grep eth # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1400' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 description 'Management Interface for the Appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible Eng Team' # set interfaces ethernet eth1 duplex 'full' # set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65' # set interfaces ethernet eth1 smp_affinity 'auto' # set interfaces ethernet eth1 speed '100' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 duplex 'full' # set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79' # set interfaces ethernet eth2 mtu '500' # set interfaces ethernet eth2 smp_affinity 'auto' # set interfaces ethernet eth2 speed '100' # set interfaces ethernet eth2 vif 200 description 'Configured by Ansible' # set interfaces ethernet eth3 description 'Configured by Ansible' # set interfaces ethernet eth3 duplex 'full' # set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85' # set interfaces ethernet eth3 mtu '1500' # set interfaces ethernet eth3 smp_affinity 'auto' # set interfaces ethernet eth3 speed '100' # set interfaces loopback lo # # - name: Replace device configurations of listed interfaces with provided configurations vyos.vyos.vyos_interfaces: config: - name: eth2 description: Replaced by Ansible - name: eth3 description: Replaced by Ansible - name: eth1 description: Replaced by Ansible state: replaced # # # ----------------------- # Module Execution Result # ----------------------- # # "before": [ # { # "description": "Bond - 1", # "enabled": true, # "mtu": 1400, # "name": "bond1" # }, # { # "enabled": true, # "name": "lo" # }, # { # "description": "Configured by Ansible", # "duplex": "full", # "enabled": true, # "mtu": 1500, # "name": "eth3", # "speed": "100" # }, # { # "description": "Configured by Ansible", # "duplex": "full", # "enabled": true, # "mtu": 500, # "name": "eth2", # "speed": "100", # "vifs": [ # { # "description": "VIF 200 - ETH2", # "enabled": true, # "vlan_id": "200" # } # ] # }, # { # "description": "Configured by Ansible Eng Team", # "duplex": "full", # "enabled": true, # "name": "eth1", # "speed": "100" # }, # { # "description": "Management Interface for the Appliance", # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # "commands": [ # "delete interfaces ethernet eth2 speed", # "delete interfaces ethernet eth2 duplex", # "delete interfaces ethernet eth2 mtu", # "delete interfaces ethernet eth2 vif 200 description", # "set interfaces ethernet eth2 description 'Replaced by Ansible'", # "delete interfaces ethernet eth3 speed", # "delete interfaces ethernet eth3 duplex", # "delete interfaces ethernet eth3 mtu", # "set interfaces ethernet eth3 description 'Replaced by Ansible'", # "delete interfaces ethernet eth1 speed", # "delete interfaces ethernet eth1 duplex", # "set interfaces ethernet eth1 description 'Replaced by Ansible'" # ] # # "after": [ # { # "description": "Bond - 1", # "enabled": true, # "mtu": 1400, # "name": "bond1" # }, # { # "enabled": true, # "name": "lo" # }, # { # "description": "Replaced by Ansible", # "enabled": true, # "name": "eth3" # }, # { # "description": "Replaced by Ansible", # "enabled": true, # "name": "eth2", # "vifs": [ # { # "enabled": true, # "vlan_id": "200" # } # ] # }, # { # "description": "Replaced by Ansible", # "enabled": true, # "name": "eth1" # }, # { # "description": "Management Interface for the Appliance", # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # # ------------- # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond1 description 'Bond - 1' # set interfaces bonding bond1 mtu '1400' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Management Interface for the Appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Replaced by Ansible' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 description 'Replaced by Ansible' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 vif 200 # set interfaces ethernet eth3 description 'Replaced by Ansible' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo # # # Using overridden # # # -------------- # Before state # -------------- # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Ethernet Interface - 0' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 mtu '1200' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible Eng Team' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 mtu '100' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth1 vif 100 description 'VIF 100 - ETH1' # set interfaces ethernet eth1 vif 100 disable # set interfaces ethernet eth2 description 'Configured by Ansible Team (Admin Down)' # set interfaces ethernet eth2 disable # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 mtu '600' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth3 description 'Configured by Ansible Network' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo # set interfaces vti vti1 description 'Virtual Tunnel Interface - 1' # set interfaces vti vti1 mtu '68' # # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_interfaces: config: - name: eth0 description: Outbound Interface For The Appliance speed: auto duplex: auto - name: eth2 speed: auto duplex: auto - name: eth3 mtu: 1200 state: overridden # # # ------------------------ # Module Execution Result # ------------------------ # # "before": [ # { # "enabled": true, # "name": "lo" # }, # { # "description": "Virtual Tunnel Interface - 1", # "enabled": true, # "mtu": 68, # "name": "vti1" # }, # { # "description": "Configured by Ansible Network", # "enabled": true, # "name": "eth3" # }, # { # "description": "Configured by Ansible Team (Admin Down)", # "enabled": false, # "mtu": 600, # "name": "eth2" # }, # { # "description": "Configured by Ansible Eng Team", # "enabled": true, # "mtu": 100, # "name": "eth1", # "vifs": [ # { # "description": "VIF 100 - ETH1", # "enabled": false, # "vlan_id": "100" # } # ] # }, # { # "description": "Ethernet Interface - 0", # "duplex": "auto", # "enabled": true, # "mtu": 1200, # "name": "eth0", # "speed": "auto" # } # ] # # "commands": [ # "delete interfaces vti vti1 description", # "delete interfaces vti vti1 mtu", # "delete interfaces ethernet eth1 description", # "delete interfaces ethernet eth1 mtu", # "delete interfaces ethernet eth1 vif 100 description", # "delete interfaces ethernet eth1 vif 100 disable", # "delete interfaces ethernet eth0 mtu", # "set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'", # "delete interfaces ethernet eth2 description", # "delete interfaces ethernet eth2 mtu", # "set interfaces ethernet eth2 duplex 'auto'", # "delete interfaces ethernet eth2 disable", # "set interfaces ethernet eth2 speed 'auto'", # "delete interfaces ethernet eth3 description", # "set interfaces ethernet eth3 mtu '1200'" # ], # # "after": [ # { # "enabled": true, # "name": "lo" # }, # { # "enabled": true, # "name": "vti1" # }, # { # "enabled": true, # "mtu": 1200, # "name": "eth3" # }, # { # "duplex": "auto", # "enabled": true, # "name": "eth2", # "speed": "auto" # }, # { # "enabled": true, # "name": "eth1", # "vifs": [ # { # "enabled": true, # "vlan_id": "100" # } # ] # }, # { # "description": "Outbound Interface For The Appliance", # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # # ------------ # After state # ------------ # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Outbound Interface For The Appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth1 vif 100 # set interfaces ethernet eth2 duplex 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 speed 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 mtu '1200' # set interfaces loopback lo # set interfaces vti vti1 # # # Using deleted # # # ------------- # Before state # ------------- # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond0 mtu '1300' # set interfaces bonding bond1 description 'LAG - 1' # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Outbound Interface for this appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible Network' # set interfaces ethernet eth1 duplex 'full' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth1 speed '100' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 disable # set interfaces ethernet eth2 duplex 'full' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 mtu '600' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth2 speed '100' # set interfaces ethernet eth3 description 'Configured by Ansible Network' # set interfaces ethernet eth3 duplex 'full' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 speed '100' # set interfaces loopback lo # # - name: Delete attributes of given interfaces (Note - This won't delete the interfaces themselves) vyos.vyos.vyos_interfaces: config: - name: bond1 - name: eth1 - name: eth2 - name: eth3 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "enabled": true, # "mtu": 1300, # "name": "bond0" # }, # { # "description": "LAG - 1", # "enabled": true, # "name": "bond1" # }, # { # "enabled": true, # "name": "lo" # }, # { # "description": "Configured by Ansible Network", # "duplex": "full", # "enabled": true, # "name": "eth3", # "speed": "100" # }, # { # "description": "Configured by Ansible", # "duplex": "full", # "enabled": false, # "mtu": 600, # "name": "eth2", # "speed": "100" # }, # { # "description": "Configured by Ansible Network", # "duplex": "full", # "enabled": true, # "name": "eth1", # "speed": "100" # }, # { # "description": "Outbound Interface for this appliance", # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # "commands": [ # "delete interfaces bonding bond1 description", # "delete interfaces ethernet eth1 speed", # "delete interfaces ethernet eth1 duplex", # "delete interfaces ethernet eth1 description", # "delete interfaces ethernet eth2 speed", # "delete interfaces ethernet eth2 disable", # "delete interfaces ethernet eth2 duplex", # "delete interfaces ethernet eth2 disable", # "delete interfaces ethernet eth2 description", # "delete interfaces ethernet eth2 disable", # "delete interfaces ethernet eth2 mtu", # "delete interfaces ethernet eth2 disable", # "delete interfaces ethernet eth3 speed", # "delete interfaces ethernet eth3 duplex", # "delete interfaces ethernet eth3 description" # ] # # "after": [ # { # "enabled": true, # "mtu": 1300, # "name": "bond0" # }, # { # "enabled": true, # "name": "bond1" # }, # { # "enabled": true, # "name": "lo" # }, # { # "enabled": true, # "name": "eth3" # }, # { # "enabled": true, # "name": "eth2" # }, # { # "enabled": true, # "name": "eth1" # }, # { # "description": "Outbound Interface for this appliance", # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # # ------------ # After state # ------------ # # vyos@vyos:~$ show configuration commands | grep interfaces # set interfaces bonding bond0 mtu '1300' # set interfaces bonding bond1 # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 description 'Outbound Interface for this appliance' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9' # set interfaces ethernet eth1 smp-affinity 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth2 smp-affinity 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces loopback lo # # # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible' # set interfaces ethernet eth1 duplex 'auto' # set interfaces ethernet eth1 mtu '1500' # set interfaces ethernet eth1 speed 'auto' # set interfaces ethernet eth1 vif 200 description 'VIF - 200' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 duplex 'auto' # set interfaces ethernet eth2 mtu '1500' # set interfaces ethernet eth2 speed 'auto' # set interfaces ethernet eth2 vif 200 description 'VIF - 200' # - name: Gather listed interfaces with provided configurations vyos.vyos.vyos_interfaces: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "description": "Configured by Ansible", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "eth2", # "speed": "auto", # "vifs": [ # { # "description": "VIF - 200", # "enabled": true, # "vlan_id": 200 # } # ] # }, # { # "description": "Configured by Ansible", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "eth1", # "speed": "auto", # "vifs": [ # { # "description": "VIF - 200", # "enabled": true, # "vlan_id": 200 # } # ] # }, # { # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] # # # After state: # ------------- # # vyos@192# run show configuration commands | grep interfaces # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 description 'Configured by Ansible' # set interfaces ethernet eth1 duplex 'auto' # set interfaces ethernet eth1 mtu '1500' # set interfaces ethernet eth1 speed 'auto' # set interfaces ethernet eth1 vif 200 description 'VIF - 200' # set interfaces ethernet eth2 description 'Configured by Ansible' # set interfaces ethernet eth2 duplex 'auto' # set interfaces ethernet eth2 mtu '1500' # set interfaces ethernet eth2 speed 'auto' # set interfaces ethernet eth2 vif 200 description 'VIF - 200' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_interfaces: config: - name: eth0 enabled: true duplex: auto speed: auto - name: eth1 description: Configured by Ansible - Interface 1 mtu: 1500 speed: auto duplex: auto enabled: true vifs: - vlan_id: 100 description: Eth1 - VIF 100 mtu: 400 enabled: true - vlan_id: 101 description: Eth1 - VIF 101 enabled: true - name: eth2 description: Configured by Ansible - Interface 2 (ADMIN DOWN) mtu: 600 enabled: false state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces ethernet eth0 duplex 'auto'", # "set interfaces ethernet eth0 speed 'auto'", # "delete interfaces ethernet eth0 disable", # "set interfaces ethernet eth1 duplex 'auto'", # "delete interfaces ethernet eth1 disable", # "set interfaces ethernet eth1 speed 'auto'", # "set interfaces ethernet eth1 description 'Configured by Ansible - Interface 1'", # "set interfaces ethernet eth1 mtu '1500'", # "set interfaces ethernet eth1 vif 100 description 'Eth1 - VIF 100'", # "set interfaces ethernet eth1 vif 100 mtu '400'", # "set interfaces ethernet eth1 vif 101 description 'Eth1 - VIF 101'", # "set interfaces ethernet eth2 disable", # "set interfaces ethernet eth2 description 'Configured by Ansible - Interface 2 (ADMIN DOWN)'", # "set interfaces ethernet eth2 mtu '600'" # ] # Using parsed # # - name: Parse the configuration. vyos.vyos.vyos_interfaces: running_config: "set interfaces ethernet eth0 address 'dhcp' set interfaces ethernet eth0 duplex 'auto' set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' set interfaces ethernet eth0 smp_affinity 'auto' set interfaces ethernet eth0 speed 'auto' set interfaces ethernet eth1 description 'Configured by Ansible' set interfaces ethernet eth1 duplex 'auto' set interfaces ethernet eth1 mtu '1500' set interfaces ethernet eth1 speed 'auto' set interfaces ethernet eth1 vif 200 description 'VIF - 200' set interfaces ethernet eth2 description 'Configured by Ansible' set interfaces ethernet eth2 duplex 'auto' set interfaces ethernet eth2 mtu '1500' set interfaces ethernet eth2 speed 'auto' set interfaces ethernet eth2 vif 200 description 'VIF - 200'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "description": "Configured by Ansible", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "eth2", # "speed": "auto", # "vifs": [ # { # "description": "VIF - 200", # "enabled": true, # "vlan_id": 200 # } # ] # }, # { # "description": "Configured by Ansible", # "duplex": "auto", # "enabled": true, # "mtu": 1500, # "name": "eth1", # "speed": "auto", # "vifs": [ # { # "description": "VIF - 200", # "enabled": true, # "vlan_id": 200 # } # ] # }, # { # "duplex": "auto", # "enabled": true, # "name": "eth0", # "speed": "auto" # } # ] """ RETURN = """ before: description: The configuration as structured data prior to module invocation. returned: always sample: > The configuration returned will always be in the same format of the parameters above. type: list after: description: The configuration as structured data after module completion. returned: when changed sample: > The configuration returned will always be in the same format of the parameters above. type: list commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - 'set interfaces ethernet eth1 mtu 1200' - 'set interfaces ethernet eth2 vif 100 description VIF 100' """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( InterfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.interfaces.interfaces import ( Interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=InterfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_l3_interfaces.py b/plugins/modules/vyos_l3_interfaces.py index 7ec9e2a..cdf5842 100644 --- a/plugins/modules/vyos_l3_interfaces.py +++ b/plugins/modules/vyos_l3_interfaces.py @@ -1,575 +1,576 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_l3_interfaces """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_l3_interfaces short_description: L3 interfaces resource module description: This module manages the L3 interface attributes on VyOS network devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Nilashish Chakraborty (@NilashishC) - Rohit Thakur (@rohitthakur2590) options: config: description: The provided L3 interfaces configuration. type: list elements: dict suboptions: name: description: - Full name of the interface, e.g. eth0, eth1. type: str required: true ipv4: description: - List of IPv4 addresses of the interface. type: list elements: dict suboptions: address: description: - IPv4 address of the interface. type: str ipv6: description: - List of IPv6 addresses of the interface. type: list elements: dict suboptions: address: description: - IPv6 address of the interface. type: str vifs: description: - Virtual sub-interfaces L3 configurations. elements: dict type: list suboptions: vlan_id: description: - Identifier for the virtual sub-interface. type: int ipv4: description: - List of IPv4 addresses of the virtual interface. type: list elements: dict suboptions: address: description: - IPv4 address of the virtual interface. type: str ipv6: description: - List of IPv6 addresses of the virtual interface. type: list elements: dict suboptions: address: description: - IPv6 address of the virtual interface. type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep -e eth[2,3]). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3] # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 # set interfaces ethernet eth3 vif 102 - name: Merge provided configuration with device configuration vyos.vyos.vyos_l3_interfaces: config: - name: eth2 ipv4: - address: 192.0.2.10/28 - address: 198.51.100.40/27 ipv6: - address: 2001:db8:100::2/32 - address: 2001:db8:400::10/32 - name: eth3 ipv4: - address: 203.0.113.65/26 vifs: - vlan_id: 101 ipv4: - address: 192.0.2.71/28 - address: 198.51.100.131/25 - vlan_id: 102 ipv6: - address: 2001:db8:1000::5/38 - address: 2001:db8:1400::3/38 state: merged # After state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3] # set interfaces ethernet eth2 address '192.0.2.10/28' # set interfaces ethernet eth2 address '198.51.100.40/27' # set interfaces ethernet eth2 address '2001:db8:100::2/32' # set interfaces ethernet eth2 address '2001:db8:400::10/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '203.0.113.65/26' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '192.0.2.71/28' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:1000::5/38' # set interfaces ethernet eth3 vif 102 address '2001:db8:1400::3/38' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' # Using replaced # # Before state: # ------------- # # vyos:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::11/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '198.51.100.10/24' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '198.51.100.130/25' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' # - name: Replace device configurations of listed interfaces with provided configurations vyos.vyos.vyos_l3_interfaces: config: - name: eth2 ipv4: - address: 192.0.2.10/24 - name: eth3 ipv6: - address: 2001:db8::11/32 state: replaced # After state: # ------------- # # vyos:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 address '2001:db8::11/32' # set interfaces ethernet eth3 vif 101 # set interfaces ethernet eth3 vif 102 # Using overridden # # Before state # -------------- # # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::11/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '198.51.100.10/24' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '198.51.100.130/25' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_l3_interfaces: config: - name: eth0 ipv4: - address: dhcp ipv6: - address: dhcpv6 state: overridden # After state # ------------ # # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 address 'dhcpv6' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 # set interfaces ethernet eth3 vif 102 # Using deleted # # Before state # ------------- # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22' # set interfaces ethernet eth0 smp-affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::11/32' # set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23' # set interfaces ethernet eth3 address '198.51.100.10/24' # set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c' # set interfaces ethernet eth3 vif 101 address '198.51.100.130/25' # set interfaces ethernet eth3 vif 101 address '198.51.100.131/25' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34' # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' - name: Delete L3 attributes of given interfaces (Note - This won't delete the interface itself) vyos.vyos.vyos_l3_interfaces: config: - name: eth1 - name: eth2 - name: eth3 state: deleted # After state # ------------ # vyos@vyos-appliance:~$ show configuration commands | grep eth # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65' # set interfaces ethernet eth1 smp_affinity 'auto' # set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79' # set interfaces ethernet eth2 smp_affinity 'auto' # set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85' # set interfaces ethernet eth3 smp_affinity 'auto' # Using gathered # # Before state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3,0] # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::12/32' # - name: Gather listed l3 interfaces with provided configurations vyos.vyos.vyos_l3_interfaces: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "ipv4": [ # { # "address": "192.0.2.11/24" # }, # { # "address": "192.0.2.10/24" # } # ], # "ipv6": [ # { # "address": "2001:db8::10/32" # }, # { # "address": "2001:db8::12/32" # } # ], # "name": "eth2" # }, # { # "ipv4": [ # { # "address": "192.0.2.14/24" # } # ], # "name": "eth1" # }, # { # "ipv4": [ # { # "address": "dhcp" # } # ], # "name": "eth0" # } # ] # # # After state: # ------------- # # vyos:~$ show configuration commands | grep -e eth[2,3] # set interfaces ethernet eth0 address 'dhcp' # set interfaces ethernet eth0 duplex 'auto' # set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' # set interfaces ethernet eth0 smp_affinity 'auto' # set interfaces ethernet eth0 speed 'auto' # set interfaces ethernet eth1 address '192.0.2.14/24' # set interfaces ethernet eth2 address '192.0.2.11/24' # set interfaces ethernet eth2 address '192.0.2.10/24' # set interfaces ethernet eth2 address '2001:db8::10/32' # set interfaces ethernet eth2 address '2001:db8::12/32' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_l3_interfaces: config: - name: eth1 ipv4: - address: 192.0.2.14/24 - name: eth2 ipv4: - address: 192.0.2.10/24 - address: 192.0.2.11/24 ipv6: - address: 2001:db8::10/32 - address: 2001:db8::12/32 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces ethernet eth1 address '192.0.2.14/24'", # "set interfaces ethernet eth2 address '192.0.2.11/24'", # "set interfaces ethernet eth2 address '192.0.2.10/24'", # "set interfaces ethernet eth2 address '2001:db8::10/32'", # "set interfaces ethernet eth2 address '2001:db8::12/32'" # ] # Using parsed # # - name: parse the provided running configuration vyos.vyos.vyos_l3_interfaces: running_config: "set interfaces ethernet eth0 address 'dhcp' set interfaces ethernet eth1 address '192.0.2.14/24' set interfaces ethernet eth2 address '192.0.2.10/24' set interfaces ethernet eth2 address '192.0.2.11/24' set interfaces ethernet eth2 address '2001:db8::10/32' set interfaces ethernet eth2 address '2001:db8::12/32'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "ipv4": [ # { # "address": "192.0.2.10/24" # }, # { # "address": "192.0.2.11/24" # } # ], # "ipv6": [ # { # "address": "2001:db8::10/32" # }, # { # "address": "2001:db8::12/32" # } # ], # "name": "eth2" # }, # { # "ipv4": [ # { # "address": "192.0.2.14/24" # } # ], # "name": "eth1" # }, # { # "ipv4": [ # { # "address": "dhcp" # } # ], # "name": "eth0" # } # ] """ RETURN = """ before: description: The configuration as structured data prior to module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The configuration as structured data after module completion. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: ['set interfaces ethernet eth1 192.0.2.14/2', 'set interfaces ethernet eth3 vif 101 address 198.51.100.130/25'] """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( L3_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.l3_interfaces.l3_interfaces import ( L3_interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=L3_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = L3_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_lag_interfaces.py b/plugins/modules/vyos_lag_interfaces.py index a530789..fb36592 100644 --- a/plugins/modules/vyos_lag_interfaces.py +++ b/plugins/modules/vyos_lag_interfaces.py @@ -1,781 +1,782 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_lag_interfaces """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_lag_interfaces short_description: LAG interfaces resource module description: This module manages attributes of link aggregation groups on VyOS network devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: Rohit Thakur (@rohitthakur2590) options: config: description: A list of link aggregation group configurations. type: list elements: dict suboptions: name: description: - Name of the link aggregation group (LAG) or bond. type: str required: true mode: description: - LAG or bond mode. type: str choices: - 802.3ad - active-backup - broadcast - round-robin - transmit-load-balance - adaptive-load-balance - xor-hash members: description: - List of member interfaces for the LAG (bond). type: list elements: dict suboptions: member: description: - Name of the member interface. type: str primary: description: - Primary device interfaces for the LAG (bond). type: str hash_policy: description: - LAG or bonding transmit hash policy. type: str choices: - layer2 - layer2+3 - layer3+4 arp_monitor: description: - ARP Link monitoring parameters. type: dict suboptions: interval: description: - ARP link monitoring frequency in milliseconds. type: int target: description: - IP address to use for ARP monitoring. type: list elements: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep bond). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 # set interfaces bonding bond3 # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lag_interfaces: config: - name: bond2 mode: active-backup members: - member: eth2 - member: eth1 hash_policy: layer2 primary: eth2 - name: bond3 mode: active-backup hash_policy: layer2+3 members: - member: eth3 primary: eth3 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "name": "bond2" # }, # { # "name": "bond3" # } # ], # # "commands": [ # "set interfaces bonding bond2 hash-policy 'layer2'", # "set interfaces bonding bond2 mode 'active-backup'", # "set interfaces ethernet eth2 bond-group bond2", # "set interfaces ethernet eth1 bond-group bond2", # "set interfaces bonding bond2 primary 'eth2'", # "set interfaces bonding bond3 hash-policy 'layer2+3'", # "set interfaces bonding bond3 mode 'active-backup'", # "set interfaces ethernet eth3 bond-group bond3", # "set interfaces bonding bond3 primary 'eth3'" # ] # # "after": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2+3' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2+3' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # - name: Replace device configurations of listed LAGs with provided configurations vyos.vyos.vyos_lag_interfaces: config: - name: bond3 mode: 802.3ad hash_policy: layer2 members: - member: eth3 state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ], # # "commands": [ # "delete interfaces bonding bond3 primary", # "set interfaces bonding bond3 hash-policy 'layer2'", # "set interfaces bonding bond3 mode '802.3ad'" # ], # # "after": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth3" # } # ], # "mode": "802.3ad", # "name": "bond3" # } # ], # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2' # set interfaces bonding bond3 mode '802.3ad' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2' # set interfaces bonding bond3 mode '802.3ad' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_lag_interfaces: config: - name: bond3 mode: active-backup members: - member: eth1 - member: eth2 - member: eth3 primary: eth3 hash_policy: layer2 state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth3" # } # ], # "mode": "802.3ad", # "name": "bond3" # } # ], # # "commands": [ # "delete interfaces bonding bond2 hash-policy", # "delete interfaces ethernet eth1 bond-group bond2", # "delete interfaces ethernet eth2 bond-group bond2", # "delete interfaces bonding bond2 mode", # "delete interfaces bonding bond2 primary", # "set interfaces bonding bond3 mode 'active-backup'", # "set interfaces ethernet eth1 bond-group bond3", # "set interfaces ethernet eth2 bond-group bond3", # "set interfaces bonding bond3 primary 'eth3'" # ], # # "after": [ # { # "name": "bond2" # }, # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # }, # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ], # # # After state # ------------ # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 # set interfaces bonding bond3 hash-policy 'layer2' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond3' # set interfaces ethernet eth2 bond-group 'bond3' # set interfaces ethernet eth3 bond-group 'bond3' # Using deleted # # Before state # ------------- # # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 hash-policy 'layer2' # set interfaces bonding bond2 mode 'active-backup' # set interfaces bonding bond2 primary 'eth2' # set interfaces bonding bond3 hash-policy 'layer2+3' # set interfaces bonding bond3 mode 'active-backup' # set interfaces bonding bond3 primary 'eth3' # set interfaces ethernet eth1 bond-group 'bond2' # set interfaces ethernet eth2 bond-group 'bond2' # set interfaces ethernet eth3 bond-group 'bond3' # - name: Delete LAG attributes of given interfaces (Note This won't delete the interface itself) vyos.vyos.vyos_lag_interfaces: config: - name: bond2 - name: bond3 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # }, # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond2", # "primary": "eth2" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth3" # } # ], # "mode": "active-backup", # "name": "bond3", # "primary": "eth3" # } # ], # "commands": [ # "delete interfaces bonding bond2 hash-policy", # "delete interfaces ethernet eth1 bond-group bond2", # "delete interfaces ethernet eth2 bond-group bond2", # "delete interfaces bonding bond2 mode", # "delete interfaces bonding bond2 primary", # "delete interfaces bonding bond3 hash-policy", # "delete interfaces ethernet eth3 bond-group bond3", # "delete interfaces bonding bond3 mode", # "delete interfaces bonding bond3 primary" # ], # # "after": [ # { # "name": "bond2" # }, # { # "name": "bond3" # } # ], # # After state # ------------ # vyos@vyos:~$ show configuration commands | grep bond # set interfaces bonding bond2 # set interfaces bonding bond3 # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep bond # set interfaces bonding bond0 hash-policy 'layer2' # set interfaces bonding bond0 mode 'active-backup' # set interfaces bonding bond0 primary 'eth1' # set interfaces bonding bond1 hash-policy 'layer2+3' # set interfaces bonding bond1 mode 'active-backup' # set interfaces bonding bond1 primary 'eth2' # set interfaces ethernet eth1 bond-group 'bond0' # set interfaces ethernet eth2 bond-group 'bond1' # - name: Gather listed lag interfaces with provided configurations vyos.vyos.vyos_lag_interfaces: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # # After state: # ------------- # # vyos@192# run show configuration commands | grep bond # set interfaces bonding bond0 hash-policy 'layer2' # set interfaces bonding bond0 mode 'active-backup' # set interfaces bonding bond0 primary 'eth1' # set interfaces bonding bond1 hash-policy 'layer2+3' # set interfaces bonding bond1 mode 'active-backup' # set interfaces bonding bond1 primary 'eth2' # set interfaces ethernet eth1 bond-group 'bond0' # set interfaces ethernet eth2 bond-group 'bond1' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lag_interfaces: config: - name: bond0 hash_policy: layer2 members: - member: eth1 mode: active-backup primary: eth1 - name: bond1 hash_policy: layer2+3 members: - member: eth2 mode: active-backup primary: eth2 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set interfaces bonding bond0 hash-policy 'layer2'", # "set interfaces ethernet eth1 bond-group 'bond0'", # "set interfaces bonding bond0 mode 'active-backup'", # "set interfaces bonding bond0 primary 'eth1'", # "set interfaces bonding bond1 hash-policy 'layer2+3'", # "set interfaces ethernet eth2 bond-group 'bond1'", # "set interfaces bonding bond1 mode 'active-backup'", # "set interfaces bonding bond1 primary 'eth2'" # ] # Using parsed # # - name: Parsed the commands for provided configuration vyos.vyos.vyos_l3_interfaces: running_config: "set interfaces bonding bond0 hash-policy 'layer2' set interfaces bonding bond0 mode 'active-backup' set interfaces bonding bond0 primary 'eth1' set interfaces bonding bond1 hash-policy 'layer2+3' set interfaces bonding bond1 mode 'active-backup' set interfaces bonding bond1 primary 'eth2' set interfaces ethernet eth1 bond-group 'bond0' set interfaces ethernet eth2 bond-group 'bond1'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "hash_policy": "layer2", # "members": [ # { # "member": "eth1" # } # ], # "mode": "active-backup", # "name": "bond0", # "primary": "eth1" # }, # { # "hash_policy": "layer2+3", # "members": [ # { # "member": "eth2" # } # ], # "mode": "active-backup", # "name": "bond1", # "primary": "eth2" # } # ] """ RETURN = """ before: description: The configuration as structured data prior to module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The configuration as structured data after module completion. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - 'set interfaces bonding bond2' - 'set interfaces bonding bond2 hash-policy layer2' """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( Lag_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lag_interfaces.lag_interfaces import ( Lag_interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lag_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lag_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_lldp_global.py b/plugins/modules/vyos_lldp_global.py index 192aa7f..f59327a 100644 --- a/plugins/modules/vyos_lldp_global.py +++ b/plugins/modules/vyos_lldp_global.py @@ -1,502 +1,503 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_lldp_global """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_lldp_global short_description: LLDP global resource module description: This module manages link layer discovery protocol (LLDP) attributes on VyOS devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: The provided link layer discovery protocol (LLDP) configuration. type: dict suboptions: enable: description: - This argument is a boolean value to enable or disable LLDP. type: bool address: description: - This argument defines management-address. type: str snmp: description: - This argument enable the SNMP queries to LLDP database. type: str legacy_protocols: description: - List of the supported legacy protocols. type: list elements: str choices: - cdp - edp - fdp - sonmp running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep lldp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state of the configuration after module completion. type: str choices: - merged - replaced - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lldp_global: config: legacy_protocols: - fdp - cdp snmp: enable address: 192.0.2.11 state: merged # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [] # # "commands": [ # "set service lldp legacy-protocols fdp", # "set service lldp legacy-protocols cdp", # "set service lldp snmp enable", # "set service lldp management-address '192.0.2.11'" # ] # # "after": [ # { # "snmp": "enable" # }, # { # "address": "192.0.2.11" # }, # { # "legacy_protocols": [ # "cdp", # "fdp" # ] # } # { # "enable": true # } # ] # # After state: # ------------- # # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # - name: Replace device configurations with provided configurations vyos.vyos.vyos_lldp_global: config: legacy_protocols: - edp - sonmp - cdp address: 192.0.2.14 state: replaced # # # ------------------------ # Module Execution Results # ------------------------ # # # "before": [ # { # "snmp": "enable" # }, # { # "address": "192.0.2.11" # }, # { # "legacy_protocols": [ # "cdp", # "fdp" # ] # } # { # "enable": true # } # ] # "commands": [ # "delete service lldp snmp", # "delete service lldp legacy-protocols fdp", # "set service lldp management-address '192.0.2.14'", # "set service lldp legacy-protocols edp", # "set service lldp legacy-protocols sonmp" # ] # # "after": [ # { # "address": "192.0.2.14" # }, # { # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # } # { # "enable": true # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # Using deleted # # Before state # ------------- # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # - name: Delete attributes of given lldp service (This won't delete the LLDP service itself) vyos.vyos.vyos_lldp_global: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "address": "192.0.2.14" # }, # { # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # } # { # "enable": true # } # ] # # "commands": [ # "delete service lldp management-address", # "delete service lldp legacy-protocols" # ] # # "after": [ # { # "enable": true # } # ] # # After state # ------------ # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # - name: Gather lldp global config with provided configurations vyos.vyos.vyos_lldp_global: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lldp_global: config: address: 192.0.2.17 enable: true legacy_protocols: - cdp state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set service lldp legacy-protocols 'cdp'", # "set service lldp", # "set service lldp management-address '192.0.2.17'" # ] # # Using parsed # # - name: Parse the provided commands to provide structured configuration vyos.vyos.vyos_lldp_global: running_config: "set service lldp legacy-protocols 'cdp' set service lldp legacy-protocols 'fdp' set service lldp management-address '192.0.2.11'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "address": "192.0.2.11", # "enable": true, # "legacy_protocols": [ # "cdp", # "fdp" # ] # } # """ RETURN = """ before: description: The configuration as structured data prior to module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The configuration as structured data after module completion. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - set service lldp legacy-protocols sonmp - set service lldp management-address '192.0.2.14' """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import ( Lldp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_global.lldp_global import ( Lldp_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lldp_globalArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lldp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_lldp_interfaces.py b/plugins/modules/vyos_lldp_interfaces.py index 43ff75c..9260cb6 100644 --- a/plugins/modules/vyos_lldp_interfaces.py +++ b/plugins/modules/vyos_lldp_interfaces.py @@ -1,673 +1,674 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_lldp_interfaces """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_lldp_interfaces short_description: LLDP interfaces resource module description: This module manages attributes of lldp interfaces on VyOS network devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A list of lldp interfaces configurations. type: list elements: dict suboptions: name: description: - Name of the lldp interface. type: str required: true enable: description: - to disable lldp on the interface. type: bool default: true location: description: - LLDP-MED location data. type: dict suboptions: civic_based: description: - Civic-based location data. type: dict suboptions: ca_info: description: LLDP-MED address info type: list elements: dict suboptions: ca_type: description: LLDP-MED Civic Address type. type: int ca_value: description: LLDP-MED Civic Address value. type: str country_code: description: Country Code type: str required: true coordinate_based: description: - Coordinate-based location. type: dict suboptions: altitude: description: Altitude in meters. type: int datum: description: Coordinate datum type. type: str choices: - WGS84 - NAD83 - MLLW latitude: description: Latitude. type: str required: true longitude: description: Longitude. type: str required: true elin: description: Emergency Call Service ELIN number (between 10-25 numbers). type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep lldp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state of the configuration after module completion. type: str choices: - merged - replaced - overridden - deleted - rendered - parsed - gathered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lldp_interfaces: config: - name: eth1 location: civic_based: country_code: US ca_info: - ca_type: 0 ca_value: ENGLISH - name: eth2 location: coordinate_based: altitude: 2200 datum: WGS84 longitude: 222.267255W latitude: 33.524449N state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set service lldp interface eth1 location civic-based country-code 'US'", # "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'", # "set service lldp interface eth1", # "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth2 location coordinate-based altitude '2200'", # "set service lldp interface eth2 location coordinate-based datum 'WGS84'", # "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", # "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth2 location coordinate-based altitude '2200'", # "set service lldp interface eth2 location coordinate-based datum 'WGS84'", # "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", # "set service lldp interface eth2" # # "after": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "civic_based": { # "ca_info": [ # { # "ca_type": 0, # "ca_value": "ENGLISH" # } # ], # "country_code": "US" # } # }, # "name": "eth1" # } # ], # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' # set service lldp interface eth1 location civic-based country-code 'US' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' # set service lldp interface eth1 location civic-based country-code 'US' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # - name: Replace device configurations of listed LLDP interfaces with provided configurations vyos.vyos.vyos_lldp_interfaces: config: - name: eth2 location: civic_based: country_code: US ca_info: - ca_type: 0 ca_value: ENGLISH - name: eth1 location: coordinate_based: altitude: 2200 datum: WGS84 longitude: 222.267255W latitude: 33.524449N state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "civic_based": { # "ca_info": [ # { # "ca_type": 0, # "ca_value": "ENGLISH" # } # ], # "country_code": "US" # } # }, # "name": "eth1" # } # ] # # "commands": [ # "delete service lldp interface eth2 location", # "set service lldp interface eth2 'disable'", # "set service lldp interface eth2 location civic-based country-code 'US'", # "set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'", # "delete service lldp interface eth1 location", # "set service lldp interface eth1 'disable'", # "set service lldp interface eth1 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth1 location coordinate-based altitude '2200'", # "set service lldp interface eth1 location coordinate-based datum 'WGS84'", # "set service lldp interface eth1 location coordinate-based longitude '222.267255W'" # ] # # "after": [ # { # "location": { # "civic_based": { # "ca_info": [ # { # "ca_type": 0, # "ca_value": "ENGLISH" # } # ], # "country_code": "US" # } # }, # "name": "eth2" # }, # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth1" # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 'disable' # set service lldp interface eth1 location coordinate-based altitude '2200' # set service lldp interface eth1 location coordinate-based datum 'WGS84' # set service lldp interface eth1 location coordinate-based latitude '33.524449N' # set service lldp interface eth1 location coordinate-based longitude '222.267255W' # set service lldp interface eth2 'disable' # set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH' # set service lldp interface eth2 location civic-based country-code 'US' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp interface eth1 'disable' # set service lldp interface eth1 location coordinate-based altitude '2200' # set service lldp interface eth1 location coordinate-based datum 'WGS84' # set service lldp interface eth1 location coordinate-based latitude '33.524449N' # set service lldp interface eth1 location coordinate-based longitude '222.267255W' # set service lldp interface eth2 'disable' # set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH' # set service lldp interface eth2 location civic-based country-code 'US' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_lldp_interfaces: config: - name: eth2 location: elin: 0000000911 state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "enable": false, # "location": { # "civic_based": { # "ca_info": [ # { # "ca_type": 0, # "ca_value": "ENGLISH" # } # ], # "country_code": "US" # } # }, # "name": "eth2" # }, # { # "enable": false, # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth1" # } # ] # # "commands": [ # "delete service lldp interface eth2 location", # "delete service lldp interface eth2 disable", # "set service lldp interface eth2 location elin 0000000911" # # # "after": [ # { # "location": { # "elin": 0000000911 # }, # "name": "eth2" # } # ] # # # After state # ------------ # # vyos@vyos# run show configuration commands | grep lldp # set service lldp interface eth2 location elin '0000000911' # Using deleted # # Before state # ------------- # # vyos@vyos# run show configuration commands | grep lldp # set service lldp interface eth2 location elin '0000000911' # - name: Delete lldp interface attributes of given interfaces. vyos.vyos.vyos_lldp_interfaces: config: - name: eth2 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # before: [{location: {elin: 0000000911}, name: eth2}] # "commands": [ # "commands": [ # "delete service lldp interface eth2" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep lldp # set service 'lldp' # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' # set service lldp interface eth1 location civic-based country-code 'US' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # - name: Gather listed lldp interfaces from running configuration vyos.vyos.vyos_lldp_interfaces: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "civic_based": { # "ca_info": [ # { # "ca_type": 0, # "ca_value": "ENGLISH" # } # ], # "country_code": "US" # } # }, # "name": "eth1" # } # ] # # # After state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' # set service lldp interface eth1 location civic-based country-code 'US' # set service lldp interface eth2 location coordinate-based altitude '2200' # set service lldp interface eth2 location coordinate-based datum 'WGS84' # set service lldp interface eth2 location coordinate-based latitude '33.524449N' # set service lldp interface eth2 location coordinate-based longitude '222.267255W' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lldp_interfaces: config: - name: eth1 location: civic_based: country_code: US ca_info: - ca_type: 0 ca_value: ENGLISH - name: eth2 location: coordinate_based: altitude: 2200 datum: WGS84 longitude: 222.267255W latitude: 33.524449N state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set service lldp interface eth1 location civic-based country-code 'US'", # "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'", # "set service lldp interface eth1", # "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", # "set service lldp interface eth2 location coordinate-based altitude '2200'", # "set service lldp interface eth2 location coordinate-based datum 'WGS84'", # "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", # "set service lldp interface eth2" # ] # Using parsed # # - name: Parsed the commands to provide structured configuration. vyos.vyos.vyos_lldp_interfaces: running_config: "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' set service lldp interface eth1 location civic-based country-code 'US' set service lldp interface eth2 location coordinate-based altitude '2200' set service lldp interface eth2 location coordinate-based datum 'WGS84' set service lldp interface eth2 location coordinate-based latitude '33.524449N' set service lldp interface eth2 location coordinate-based longitude '222.267255W'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "location": { # "coordinate_based": { # "altitude": 2200, # "datum": "WGS84", # "latitude": "33.524449N", # "longitude": "222.267255W" # } # }, # "name": "eth2" # }, # { # "location": { # "civic_based": { # "ca_info": [ # { # "ca_type": 0, # "ca_value": "ENGLISH" # } # ], # "country_code": "US" # } # }, # "name": "eth1" # } # ] """ RETURN = """ before: description: The configuration as structured data prior to module invocation. returned: always type: list sample: > The configuration returned will always be in the same format of the parameters above. after: description: The configuration as structured data after module completion. returned: when changed type: list sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set service lldp interface eth2 'disable'" - "delete service lldp interface eth1 location" """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import ( Lldp_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_interfaces.lldp_interfaces import ( Lldp_interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lldp_interfacesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lldp_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_logging.py b/plugins/modules/vyos_logging.py index 4b3eaaf..7e1dea7 100644 --- a/plugins/modules/vyos_logging.py +++ b/plugins/modules/vyos_logging.py @@ -1,342 +1,334 @@ #!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function __metaclass__ = type # (c) 2017, Ansible by Red Hat, inc # # This file is part of Ansible by Red Hat # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # DOCUMENTATION = """ module: vyos_logging author: Trishna Guha (@trishnaguha) short_description: Manage logging on network devices description: - This module provides declarative management of logging on Vyatta Vyos devices. version_added: 1.0.0 deprecated: alternative: vyos_logging_global why: Updated module released with more functionality. removed_at_date: '2023-08-01' notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: dest: description: - Destination of the logs. type: str choices: - console - file - global - host - user name: description: - If value of C(dest) is I(file) it indicates file-name, for I(user) it indicates username and for I(host) indicates the host name to be notified. type: str facility: description: - Set logging facility. type: str level: description: - Set logging severity levels. type: str aggregate: description: List of logging definitions. type: list elements: dict suboptions: dest: description: - Destination of the logs. type: str choices: - console - file - global - host - user name: description: - If value of C(dest) is I(file) it indicates file-name, for I(user) it indicates username and for I(host) indicates the host name to be notified. type: str facility: description: - Set logging facility. type: str level: description: - Set logging severity levels. type: str state: description: - State of the logging configuration. type: str choices: - present - absent state: description: - State of the logging configuration. type: str default: present choices: - present - absent extends_documentation_fragment: - vyos.vyos.vyos """ EXAMPLES = """ - name: configure console logging vyos.vyos.vyos_logging: dest: console facility: all level: crit - name: remove console logging configuration vyos.vyos.vyos_logging: dest: console state: absent - name: configure file logging vyos.vyos.vyos_logging: dest: file name: test facility: local3 level: err - name: Add logging aggregate vyos.vyos.vyos_logging: aggregate: - {dest: file, name: test1, facility: all, level: info} - {dest: file, name: test2, facility: news, level: debug} state: present - name: Remove logging aggregate vyos.vyos.vyos_logging: aggregate: - {dest: console, facility: all, level: info} - {dest: console, facility: daemon, level: warning} - {dest: file, name: test2, facility: news, level: debug} state: absent """ RETURN = """ commands: description: The list of configuration mode commands to send to the device returned: always type: list sample: - set system syslog global facility all level notice """ import re - from copy import deepcopy from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.validation import check_required_if from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_default_spec, ) + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_config, load_config, ) def spec_to_commands(updates, module): commands = list() want, have = updates for w in want: dest = w["dest"] name = w["name"] facility = w["facility"] level = w["level"] state = w["state"] del w["state"] if state == "absent" and w in have: if w["name"]: commands.append( "delete system syslog {0} {1} facility {2} level {3}".format( dest, name, facility, level ) ) else: commands.append( - "delete system syslog {0} facility {1} level {2}".format( - dest, facility, level - ) + "delete system syslog {0} facility {1} level {2}".format(dest, facility, level) ) elif state == "present" and w not in have: if w["name"]: commands.append( "set system syslog {0} {1} facility {2} level {3}".format( dest, name, facility, level ) ) else: commands.append( - "set system syslog {0} facility {1} level {2}".format( - dest, facility, level - ) + "set system syslog {0} facility {1} level {2}".format(dest, facility, level) ) return commands def config_to_dict(module): data = get_config(module) obj = [] for line in data.split("\n"): if line.startswith("set system syslog"): match = re.search(r"set system syslog (\S+)", line, re.M) dest = match.group(1) if dest == "host": match = re.search(r"host (\S+)", line, re.M) name = match.group(1) elif dest == "file": match = re.search(r"file (\S+)", line, re.M) name = match.group(1) elif dest == "user": match = re.search(r"user (\S+)", line, re.M) name = match.group(1) else: name = None if "facility" in line: match = re.search(r"facility (\S+)", line, re.M) facility = match.group(1) if "level" in line: match = re.search(r"level (\S+)", line, re.M) level = match.group(1).strip("'") obj.append( { "dest": dest, "name": name, "facility": facility, "level": level, } ) return obj def map_params_to_obj(module, required_if=None): obj = [] aggregate = module.params.get("aggregate") if aggregate: for item in aggregate: for key in item: if item.get(key) is None: item[key] = module.params[key] try: check_required_if(required_if, item) except TypeError as exc: module.fail_json(to_text(exc)) obj.append(item.copy()) else: if module.params["dest"] not in ("host", "file", "user"): module.params["name"] = None obj.append( { "dest": module.params["dest"], "name": module.params["name"], "facility": module.params["facility"], "level": module.params["level"], "state": module.params["state"], } ) return obj def main(): """main entry point for module execution""" element_spec = dict( - dest=dict( - type="str", choices=["console", "file", "global", "host", "user"] - ), + dest=dict(type="str", choices=["console", "file", "global", "host", "user"]), name=dict(type="str"), facility=dict(type="str"), level=dict(type="str"), state=dict(default="present", choices=["present", "absent"]), ) aggregate_spec = deepcopy(element_spec) # remove default in aggregate spec, to handle common arguments remove_default_spec(aggregate_spec) - argument_spec = dict( - aggregate=dict(type="list", elements="dict", options=aggregate_spec) - ) + argument_spec = dict(aggregate=dict(type="list", elements="dict", options=aggregate_spec)) argument_spec.update(element_spec) required_if = [ ("dest", "host", ["name", "facility", "level"]), ("dest", "file", ["name", "facility", "level"]), ("dest", "user", ["name", "facility", "level"]), ("dest", "console", ["facility", "level"]), ("dest", "global", ["facility", "level"]), ] module = AnsibleModule( argument_spec=argument_spec, required_if=required_if, supports_check_mode=True, ) warnings = list() result = {"changed": False} if warnings: result["warnings"] = warnings want = map_params_to_obj(module, required_if=required_if) have = config_to_dict(module) commands = spec_to_commands((want, have), module) result["commands"] = commands if commands: commit = not module.check_mode load_config(module, commands, commit=commit) result["changed"] = True module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_logging_global.py b/plugins/modules/vyos_logging_global.py index 6a4e2dd..18248d6 100644 --- a/plugins/modules/vyos_logging_global.py +++ b/plugins/modules/vyos_logging_global.py @@ -1,776 +1,777 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_logging_global """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_logging_global version_added: 2.4.0 short_description: Logging resource module description: This module manages the logging attributes of Vyos network devices author: Sagar Paul (@KB-perByte) notes: - Tested against vyos 1.2 - This module works with connection C(network_cli). - The Configuration defaults of the Vyos network devices are supposed to hinder idempotent behavior of plays options: config: description: A list containing dictionary of logging options type: dict suboptions: console: description: logging to serial console type: dict suboptions: state: &state_config description: enable or disable the command type: str choices: - enabled - disabled facilities: description: facility configurations for console type: list elements: dict suboptions: facility: &facility description: Facility for logging type: str choices: - all - auth - authpriv - cron - daemon - kern - lpr - mail - mark - news - protocols - security - syslog - user - uucp - local0 - local1 - local2 - local3 - local4 - local5 - local6 - local7 severity: &severity description: logging level type: str choices: - emerg - alert - crit - err - warning - notice - info - debug - all files: description: logging to file type: list elements: dict suboptions: path: description: file name or path type: str archive: &archive description: Log file size and rotation characteristics type: dict suboptions: state: *state_config file_num: description: Number of saved files (default is 5) type: int size: description: Size of log files (in kilobytes, default is 256) type: int facilities: ¶ms description: facility configurations type: list elements: dict suboptions: facility: *facility severity: *severity global_params: description: logging to serial console type: dict suboptions: state: *state_config archive: *archive facilities: *params marker_interval: description: time interval how often a mark message is being sent in seconds (default is 1200) type: int preserve_fqdn: description: uses FQDN for logging type: bool hosts: description: logging to serial console type: list elements: dict suboptions: port: description: Destination port (1-65535) type: int facilities: description: facility configurations for host type: list elements: dict suboptions: facility: *facility severity: *severity protocol: description: syslog communication protocol type: str choices: - udp - tcp hostname: description: Remote host name or IP address type: str syslog: description: logging syslog type: dict suboptions: state: *state_config users: description: logging to file type: list elements: dict suboptions: username: description: user login name type: str facilities: *params running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration commands | grep syslog). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: choices: - merged - replaced - overridden - deleted - gathered - parsed - rendered default: merged description: - The state the configuration should be left in - The states I(replaced) and I(overridden) have identical behaviour for this module. - Refer to examples for more details. type: str """ EXAMPLES = """ # Using state: merged # Before state: # ------------- # vyos:~$show configuration commands | grep syslog - name: Apply the provided configuration vyos.vyos.vyos_logging_global: config: console: facilities: - facility: local7 severity: err files: - path: logFile archive: file_num: 2 facilities: - facility: local6 severity: emerg hosts: - hostname: 172.16.0.1 facilities: - facility: local7 severity: all - facility: all protocol: udp port: 223 users: - username: vyos facilities: - facility: local7 severity: debug global_params: archive: file_num: 2 size: 111 facilities: - facility: cron severity: debug marker_interval: 111 preserve_fqdn: true state: merged # Commands Fired: # --------------- # "commands": [ # "set system syslog console facility local7 level err", # "set system syslog file logFile archive file 2", # "set system syslog host 172.16.0.1 facility local7 level all", # "set system syslog file logFile facility local6 level emerg", # "set system syslog host 172.16.0.1 facility all protocol udp", # "set system syslog user vyos facility local7 level debug", # "set system syslog host 172.16.0.1 port 223", # "set system syslog global facility cron level debug", # "set system syslog global archive file 2", # "set system syslog global archive size 111", # "set system syslog global marker interval 111", # "set system syslog global preserve-fqdn" # ], # After state: # ------------ # vyos:~$ show configuration commands | grep syslog # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user vyos facility local7 level 'debug' # Using state: deleted # Before state: # ------------- # vyos:~$show configuration commands | grep syslog # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user vyos facility local7 level 'debug' - name: delete the existing configuration vyos.vyos.vyos_logging_global: state: deleted # Commands Fired: # --------------- # "commands": [ # "delete system syslog" # ], # After state: # ------------ # vyos:~$show configuration commands | grep syslog # Using state: overridden # Before state: # ------------- # vyos:~$show configuration commands | grep syslog # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user vyos facility local7 level 'debug' - name: Override the current configuration vyos.vyos.vyos_logging_global: config: console: facilities: - facility: all - facility: local7 severity: err - facility: news severity: debug files: - path: logFileNew hosts: - hostname: 172.16.0.2 facilities: - facility: local5 severity: all global_params: archive: file_num: 10 state: overridden # Commands Fired: # --------------- # "commands": [ # "delete system syslog file logFile", # "delete system syslog global facility cron", # "delete system syslog host 172.16.0.1", # "delete system syslog user vyos", # "set system syslog console facility all", # "set system syslog console facility news level debug", # "set system syslog file logFileNew", # "set system syslog host 172.16.0.2 facility local5 level all", # "set system syslog global archive file 10", # "delete system syslog global archive size 111", # "delete system syslog global marker", # "delete system syslog global preserve-fqdn" # ], # After state: # ------------ # vyos:~$show configuration commands | grep syslog # set system syslog console facility all # set system syslog console facility local7 level 'err' # set system syslog console facility news level 'debug' # set system syslog file logFileNew # set system syslog global archive file '10' # set system syslog host 172.16.0.2 facility local5 level 'all' # Using state: replaced # Before state: # ------------- # vyos:~$show configuration commands | grep syslog # set system syslog console facility all # set system syslog console facility local7 level 'err' # set system syslog console facility news level 'debug' # set system syslog file logFileNew # set system syslog global archive file '10' # set system syslog host 172.16.0.2 facility local5 level 'all' - name: Replace with the provided configuration register: result vyos.vyos.vyos_logging_global: config: console: facilities: - facility: local6 users: - username: paul facilities: - facility: local7 severity: err state: replaced # Commands Fired: # --------------- # "commands": [ # "delete system syslog console facility all", # "delete system syslog console facility local7", # "delete system syslog console facility news", # "delete system syslog file logFileNew", # "delete system syslog global archive file 10", # "delete system syslog host 172.16.0.2", # "set system syslog console facility local6", # "set system syslog user paul facility local7 level err" # ], # After state: # ------------ # vyos:~$show configuration commands | grep syslog # set system syslog console facility local6 # set system syslog user paul facility local7 level 'err' # Using state: gathered - name: Gather logging config vyos.vyos.vyos_logging_global: state: gathered # Module Execution Result: # ------------------------ # "gathered": { # "console": { # "facilities": [ # { # "facility": "local6" # }, # { # "facility": "local7", # "severity": "err" # } # ] # }, # "files": [ # { # "archive": { # "file_num": 2 # }, # "facilities": [ # { # "facility": "local6", # "severity": "emerg" # } # ], # "path": "logFile" # } # ], # "global_params": { # "archive": { # "file_num": 2, # "size": 111 # }, # "facilities": [ # { # "facility": "cron", # "severity": "debug" # } # ], # "marker_interval": 111, # "preserve_fqdn": true # }, # "hosts": [ # { # "facilities": [ # { # "facility": "all", # "protocol": "udp" # }, # { # "facility": "local7", # "severity": "all" # } # ], # "hostname": "172.16.0.1", # "port": 223 # } # ], # "users": [ # { # "facilities": [ # { # "facility": "local7", # "severity": "err" # } # ], # "username": "paul" # }, # { # "facilities": [ # { # "facility": "local7", # "severity": "debug" # } # ], # "username": "vyos" # } # ] # }, # After state: # ------------ # vyos:~$show configuration commands | grep syslog # set system syslog console facility local6 # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user paul facility local7 level 'err' # set system syslog user vyos facility local7 level 'debug' # Using state: rendered - name: Render the provided configuration vyos.vyos.vyos_logging_global: config: console: facilities: - facility: local7 severity: err files: - path: logFile archive: file_num: 2 facilities: - facility: local6 severity: emerg hosts: - hostname: 172.16.0.1 facilities: - facility: local7 severity: all - facility: all protocol: udp port: 223 users: - username: vyos facilities: - facility: local7 severity: debug global_params: archive: file_num: 2 size: 111 facilities: - facility: cron severity: debug marker_interval: 111 preserve_fqdn: true state: rendered # Module Execution Result: # ------------------------ # "rendered": [ # "set system syslog console facility local7 level err", # "set system syslog file logFile facility local6 level emerg", # "set system syslog file logFile archive file 2", # "set system syslog host 172.16.0.1 facility local7 level all", # "set system syslog host 172.16.0.1 facility all protocol udp", # "set system syslog host 172.16.0.1 port 223", # "set system syslog user vyos facility local7 level debug", # "set system syslog global facility cron level debug", # "set system syslog global archive file 2", # "set system syslog global archive size 111", # "set system syslog global marker interval 111", # "set system syslog global preserve-fqdn" # ] # Using state: parsed # File: parsed.cfg # ---------------- # set system syslog console facility local6 # set system syslog console facility local7 level 'err' # set system syslog file logFile archive file '2' # set system syslog file logFile facility local6 level 'emerg' # set system syslog global archive file '2' # set system syslog global archive size '111' # set system syslog global facility cron level 'debug' # set system syslog global marker interval '111' # set system syslog global preserve-fqdn # set system syslog host 172.16.0.1 facility all protocol 'udp' # set system syslog host 172.16.0.1 facility local7 level 'all' # set system syslog host 172.16.0.1 port '223' # set system syslog user paul facility local7 level 'err' # set system syslog user vyos facility local7 level 'debug' - name: Parse the provided configuration vyos.vyos.vyos_logging_global: running_config: "{{ lookup('file', 'parsed_vyos.cfg') }}" state: parsed # Module Execution Result: # ------------------------ # "parsed": { # "console": { # "facilities": [ # { # "facility": "local6" # }, # { # "facility": "local7", # "severity": "err" # } # ] # }, # "files": [ # { # "archive": { # "file_num": 2 # }, # "facilities": [ # { # "facility": "local6", # "severity": "emerg" # } # ], # "path": "logFile" # } # ], # "global_params": { # "archive": { # "file_num": 2, # "size": 111 # }, # "facilities": [ # { # "facility": "cron", # "severity": "debug" # } # ], # "marker_interval": 111, # "preserve_fqdn": true # }, # "hosts": [ # { # "facilities": [ # { # "facility": "all", # "protocol": "udp" # }, # { # "facility": "local7", # "severity": "all" # } # ], # "hostname": "172.16.0.1", # "port": 223 # } # ], # "users": [ # { # "facilities": [ # { # "facility": "local7", # "severity": "err" # } # ], # "username": "paul" # }, # { # "facilities": [ # { # "facility": "local7", # "severity": "debug" # } # ], # "username": "vyos" # } # ] # } # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when state is I(merged), I(replaced), I(overridden), I(deleted) or I(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when state is I(merged), I(replaced), I(overridden), I(deleted) or I(purged) type: list sample: - "set system syslog console facility local7 level err" - "set system syslog host 172.16.0.1 port 223" - "set system syslog global archive size 111" rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when state is I(rendered) type: list sample: - "set system syslog host 172.16.0.1 port 223" - "set system syslog user vyos facility local7 level debug" - "set system syslog global facility cron level debug" gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when state is I(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when state is I(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.logging_global.logging_global import ( Logging_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.logging_global.logging_global import ( Logging_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Logging_globalArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Logging_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ntp_global.py b/plugins/modules/vyos_ntp_global.py index d06d878..10a2170 100644 --- a/plugins/modules/vyos_ntp_global.py +++ b/plugins/modules/vyos_ntp_global.py @@ -1,815 +1,816 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The module file for vyos_ntp_global """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_ntp_global version_added: 2.4.0 short_description: Manages ntp modules of Vyos network devices description: - This module manages ntp configuration on devices running Vyos author: Varshitha Yataluru (@YVarshitha) notes: - Tested against vyos 1.3 - This module works with connection C(network_cli). options: config: description: List of configurations for ntp module type: dict suboptions: allow_clients: description: Network Time Protocol (NTP) server options type: list elements: str listen_addresses: description: local IP addresses for service to listen on type: list elements: str servers: description: Network Time Protocol (NTP) server type: list elements: dict suboptions: server: description: server name for NTP type: str options: description: server options for NTP type: list elements: str choices: - noselect - dynamic - pool - preempt - prefer running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration commands | grep ntp). - The states I(replaced) and I(overridden) have identical behaviour for this module. - The state I(parsed) reads the configuration from C(show configuration commands | grep ntp) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - deleted - merged - overridden - replaced - gathered - rendered - parsed default: merged """ EXAMPLES = """ # # ------------------- # # 1. Using merged # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Replace the existing ntp config with the new config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.6.6.0/24 listen_addresses: - 10.1.3.1 servers: - server: 203.0.113.0 options: - prefer # # Task output: # # ------------- # "after": { # "allow_clients": [ # "10.6.6.0/24" # ], # "listen_addresses": [ # "10.1.3.1" # ], # "servers": [ # { # "server": "ser", # "options": [ # "prefer" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # }, # "changed": true, # "commands": [ # "set system ntp allow-clients address 10.6.6.0/24", # "set system ntp listen-address 10.1.3.1", # "set system ntp server 203.0.113.0 prefer" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.6.6.0/24' # set system ntp listen-address '10.1.3.1' # set system ntp server 203.0.113.0 prefer, # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 2. Using replaced # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.4.9.0/24' # set system ntp allow-clients address '10.4.7.0/24' # set system ntp allow-clients address '10.1.2.0/24' # set system ntp allow-clients address '10.2.3.0/24' # set system ntp listen-address '10.1.9.16' # set system ntp listen-address '10.5.3.2' # set system ntp listen-address '10.7.9.21' # set system ntp listen-address '10.8.9.4' # set system ntp listen-address '10.4.5.1' # set system ntp server 10.3.6.5 noselect # set system ntp server 10.3.6.5 dynamic # set system ntp server 10.3.6.5 preempt # set system ntp server 10.3.6.5 prefer # set system ntp server server4 noselect # set system ntp server server4 dynamic # set system ntp server server5 # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Replace the existing ntp config with the new config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.6.6.0/24 listen_addresses: - 10.1.3.1 servers: - server: 203.0.113.0 options: - prefer state: replaced # # Task output: # # ------------- # "after": { # "allow_clients": [ # "10.6.6.0/24" # ], # "listen_addresses": [ # "10.1.3.1" # ], # "servers": [ # { # "server": "ser", # "options": [ # "prefer" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # "allow_clients": [ # "10.4.7.0/24", # "10.2.3.0/24", # "10.1.2.0/24", # "10.4.9.0/24" # ], # "listen_addresses": [ # "10.7.9.21", # "10.4.5.1", # "10.5.3.2", # "10.8.9.4", # "10.1.9.16" # ], # "servers": [ # { # "server": "10.3.6.5", # "options": [ # "noselect", # "dynamic", # "preempt", # "prefer" # ] # }, # { # "server": "server4", # "options": [ # "noselect", # "dynamic" # ] # }, # { # "server": "server5" # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "changed": true, # "commands": [ # "delete system ntp allow-clients address 10.4.7.0/24", # "delete system ntp allow-clients address 10.2.3.0/24", # "delete system ntp allow-clients address 10.1.2.0/24", # "delete system ntp allow-clients address 10.4.9.0/24", # "delete system ntp listen-address 10.7.9.21", # "delete system ntp listen-address 10.4.5.1", # "delete system ntp listen-address 10.5.3.2", # "delete system ntp listen-address 10.8.9.4", # "delete system ntp listen-address 10.1.9.16", # "delete system ntp server 10.3.6.5", # "delete system ntp server server4", # "delete system ntp server server5", # "set system ntp allow-clients address 10.6.6.0/24", # "set system ntp listen-address 10.1.3.1", # "set system ntp server 203.0.113.0 prefer" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.6.6.0/24' # set system ntp listen-address '10.1.3.1' # set system ntp server 203.0.113.0 prefer, # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 3. Using overridden # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.6.6.0/24' # set system ntp listen-address '10.1.3.1' # set system ntp server 203.0.113.0 prefer, # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Override ntp config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.3.3.0/24 listen_addresses: - 10.7.8.1 servers: - server: server1 options: - dynamic - prefer - server: server2 options: - noselect - preempt - server: serv state: overridden # # Task output: # # ------------- # "after": { # "allow_clients": [ # "10.3.3.0/24" # ], # "listen_addresses": [ # "10.7.8.1" # ], # "servers": [ # { # "server": "serv" # }, # { # "server": "server1", # "options": [ # "dynamic", # "prefer" # ] # }, # { # "server": "server2", # "options": [ # "noselect", # "preempt" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # "allow_clients": [ # "10.6.6.0/24" # ], # "listen_addresses": [ # "10.1.3.1" # ], # "servers": [ # { # "server": "ser", # "options": [ # "prefer" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "changed": true, # "commands": [ # "delete system ntp allow-clients address 10.6.6.0/24", # "delete system ntp listen-address 10.1.3.1", # "delete system ntp server ser", # "set system ntp allow-clients address 10.3.3.0/24", # "set system ntp listen-address 10.7.8.1", # "set system ntp server server1 dynamic", # "set system ntp server server1 prefer", # "set system ntp server server2 noselect", # "set system ntp server server2 preempt", # "set system ntp server serv" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.3.3.0/24' # set system ntp listen-address '10.7.8.1' # set system ntp server serv # set system ntp server server1 dynamic # set system ntp server server1 prefer # set system ntp server server2 noselect # set system ntp server server2 preempt # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 4. Using gathered # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.3.3.0/24' # set system ntp listen-address '10.7.8.1' # set system ntp server serv # set system ntp server server1 dynamic # set system ntp server server1 prefer # set system ntp server server2 noselect # set system ntp server server2 preempt # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Gather ntp config vyos.vyos.vyos_ntp_global: state: gathered # # Task output: # # ------------- # "gathered": { # "allow_clients": [ # "10.3.3.0/24" # ], # "listen_addresses": [ # "10.7.8.1" # ], # "servers": [ # { # "server": "serv" # }, # { # "server": "server1", # "options": [ # "dynamic", # "prefer" # ] # }, # { # "server": "server2", # "options": [ # "noselect", # "preempt" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # } # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.3.3.0/24' # set system ntp listen-address '10.7.8.1' # set system ntp server serv # set system ntp server server1 dynamic # set system ntp server server1 prefer # set system ntp server server2 noselect # set system ntp server server2 preempt # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 5. Using deleted # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp allow-clients address '10.3.3.0/24' # set system ntp listen-address '10.7.8.1' # set system ntp server serv # set system ntp server server1 dynamic # set system ntp server server1 prefer # set system ntp server server2 noselect # set system ntp server server2 preempt # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Delete ntp config vyos.vyos.vyos_ntp_global: state: deleted # # Task output: # # ------------- # "after": { # "servers": [ # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "before": { # "allow_clients": [ # "10.3.3.0/24" # ], # "listen_addresses": [ # "10.7.8.1" # ], # "servers": [ # { # "server": "serv" # }, # { # "server": "server1", # "options": [ # "dynamic", # "prefer" # ] # }, # { # "server": "server2", # "options": [ # "noselect", # "preempt" # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # ] # }, # "changed": true, # "commands": [ # "delete system ntp allow-clients", # "delete system ntp listen-address", # "delete system ntp server serv", # "delete system ntp server server1", # "delete system ntp server server2" # # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # ------------------- # # 6. Using rendered # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | grep ntp # set system ntp server time1.vyos.net # set system ntp server time2.vyos.net # set system ntp server time3.vyos.net # vyos@vyos:~$ # # Task # # ------------- - name: Render ntp config vyos.vyos.vyos_ntp_global: config: allow_clients: - 10.7.7.0/24 - 10.8.8.0/24 listen_addresses: - 10.7.9.1 servers: - server: server7 - server: server45 options: - noselect - prefer - pool - server: time1.vyos.net - server: time2.vyos.net - server: time3.vyos.net state: rendered # # Task output: # # ------------- # "rendered": [ # "set system ntp allow-clients address 10.7.7.0/24", # "set system ntp allow-clients address 10.8.8.0/24", # "set system ntp listen-address 10.7.9.1", # "set system ntp server server7", # "set system ntp server server45 noselect", # "set system ntp server server45 prefer", # "set system ntp server server45 pool", # "set system ntp server time1.vyos.net", # "set system ntp server time2.vyos.net", # "set system ntp server time3.vyos.net" # ] # # ------------------- # # 7. Using parsed # # ------------------- # # sample_config.cfg: # # ------------- # "set system ntp allow-clients address 10.7.7.0/24", # "set system ntp listen-address 10.7.9.1", # "set system ntp server server45 noselect", # "set system ntp allow-clients addres 10.8.6.0/24", # "set system ntp listen-address 10.5.4.1", # "set system ntp server server45 dynamic", # "set system ntp server time1.vyos.net", # "set system ntp server time2.vyos.net", # "set system ntp server time3.vyos.net" # # Task: # # ------------- - name: Parse externally provided ntp configuration vyos.vyos.vyos_ntp_global: running_config: "{{ lookup('file', './sample_config.cfg') }}" state: parsed # # Task output: # # ------------- # parsed = { # "allow_clients": [ # "10.7.7.0/24", # "10.8.6.0/24 # ], # "listen_addresses": [ # "10.5.4.1", # "10.7.9.1" # ], # "servers": [ # { # "server": "server45", # "options": [ # "noselect", # "dynamic" # # ] # }, # { # "server": "time1.vyos.net" # }, # { # "server": "time2.vyos.net" # }, # { # "server": "time3.vyos.net" # } # # ] # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - set system ntp server server1 dynamic - set system ntp server server1 prefer - set system ntp server server2 noselect - set system ntp server server2 preempt - set system ntp server server_add preempt rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - set system ntp server server1 dynamic - set system ntp server server1 prefer - set system ntp server server2 noselect - set system ntp server server2 preempt - set system ntp server server_add preempt gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ntp_global.ntp_global import ( Ntp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ntp_global.ntp_global import ( Ntp_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Ntp_globalArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Ntp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ospf_interfaces.py b/plugins/modules/vyos_ospf_interfaces.py index 61ef32e..9a78ebd 100644 --- a/plugins/modules/vyos_ospf_interfaces.py +++ b/plugins/modules/vyos_ospf_interfaces.py @@ -1,917 +1,918 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2020 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_ospf_interfaces """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_ospf_interfaces version_added: 1.2.0 short_description: OSPF Interfaces Resource Module. description: - This module manages OSPF configuration of interfaces on devices running VYOS. author: Gomathi Selvi Srinivasan (@GomathiselviS) options: config: description: A list of OSPF configuration for interfaces. type: list elements: dict suboptions: name: description: - Name/Identifier of the interface. type: str address_family: description: - OSPF settings on the interfaces in address-family context. type: list elements: dict suboptions: afi: description: - Address Family Identifier (AFI) for OSPF settings on the interfaces. type: str choices: ['ipv4', 'ipv6'] required: True authentication: description: - Authentication settings on the interface. type: dict suboptions: plaintext_password: description: - Plain Text password. type: str md5_key: description: - md5 parameters. type: dict suboptions: key_id: description: - key id. type: int key: description: - md5 key. type: str bandwidth: description: - Bandwidth of interface (kilobits/sec) type: int cost: description: - metric associated with interface. type: int dead_interval: description: - Time interval to detect a dead router. type: int hello_interval: description: - Timer interval between transmission of hello packets. type: int mtu_ignore: description: - if True, Disable MTU check for Database Description packets. type: bool network: description: - Interface type. type: str priority: description: - Interface priority. type: int retransmit_interval: description: - LSA retransmission interval. type: int transmit_delay: description: - LSA transmission delay. type: int ifmtu: description: - interface MTU. type: int instance: description: - Instance ID. type: str passive: description: - If True, disables forming adjacency. type: bool running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration commands | match "set interfaces"). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - overridden - deleted - gathered - parsed - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # @vyos:~$ show configuration commands | match "ospf" - name: Merge provided configuration with device configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth1" address_family: - afi: "ipv4" transmit_delay: 50 priority: 26 network: "point-to-point" - afi: "ipv6" dead_interval: 39 - name: "bond2" address_family: - afi: "ipv4" transmit_delay: 45 bandwidth: 70 authentication: md5_key: key_id: 10 key: "1111111111232345" - afi: "ipv6" passive: True state: merged # After State: # -------------- # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' # "after": [ # " # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "set interfaces ethernet eth1 ip ospf transmit-delay 50", # "set interfaces ethernet eth1 ip ospf priority 26", # "set interfaces ethernet eth1 ip ospf network point-to-point", # "set interfaces ethernet eth1 ipv6 ospfv3 dead-interval 39", # "set interfaces bonding bond2 ip ospf transmit-delay 45", # "set interfaces bonding bond2 ip ospf bandwidth 70", # "set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key 1111111111232345", # "set interfaces bonding bond2 ipv6 ospfv3 passive" # ], # Using replaced: # Before State: # ------------ # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' - name: Replace provided configuration with device configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth1" address_family: - afi: "ipv4" cost: 100 - afi: "ipv6" ifmtu: 33 - name: "bond2" address_family: - afi: "ipv4" transmit_delay: 45 - afi: "ipv6" passive: True state: replaced # After State: # ----------- # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf cost '100' # set interfaces ethernet eth1 ipv6 ospfv3 ifmtu '33' # vyos@vyos:~$ # Module Execution # ---------------- # "after": [ # { # "address_family": [ # { # "afi": "ipv4", # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100 # }, # { # "afi": "ipv6", # "ifmtu": 33 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "set interfaces ethernet eth1 ip ospf cost 100", # "set interfaces ethernet eth1 ipv6 ospfv3 ifmtu 33", # "delete interfaces ethernet eth1 ip ospf network point-to-point", # "delete interfaces ethernet eth1 ip ospf priority 26", # "delete interfaces ethernet eth1 ip ospf transmit-delay 50", # "delete interfaces ethernet eth1 ipv6 ospfv3 dead-interval 39", # "delete interfaces bonding bond2 ip ospf authentication", # "delete interfaces bonding bond2 ip ospf bandwidth 70" # ], # # Using Overridden: # ----------------- # Before State: # ------------ # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf cost '100' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' # set interfaces ethernet eth1 ipv6 ospfv3 ifmtu '33' # vyos@vyos:~$ - name: Override device configuration with provided configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth0" address_family: - afi: "ipv4" cost: 100 - afi: "ipv6" ifmtu: 33 passive: True state: overridden # After State: # ----------- # 200~vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces ethernet eth0 ip ospf cost '100' # set interfaces ethernet eth0 ipv6 ospfv3 ifmtu '33' # set interfaces ethernet eth0 ipv6 ospfv3 'passive' # vyos@vyos:~$ # # # "after": [ # { # "name": "bond2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100 # }, # { # "afi": "ipv6", # "ifmtu": 33, # "passive": true # } # ], # "name": "eth0" # }, # { # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100, # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39, # "ifmtu": 33 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "delete interfaces bonding bond2 ip ospf", # "delete interfaces bonding bond2 ipv6 ospfv3", # "delete interfaces ethernet eth1 ip ospf", # "delete interfaces ethernet eth1 ipv6 ospfv3", # "set interfaces ethernet eth0 ip ospf cost 100", # "set interfaces ethernet eth0 ipv6 ospfv3 ifmtu 33", # "set interfaces ethernet eth0 ipv6 ospfv3 passive" # ], # # Using deleted: # ------------- # before state: # ------------- # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth0 ip ospf cost '100' # set interfaces ethernet eth0 ipv6 ospfv3 ifmtu '33' # set interfaces ethernet eth0 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' # vyos@vyos:~$ - name: Delete device configuration vyos.vyos.vyos_ospf_interfaces: config: - name: "eth0" state: deleted # After State: # ----------- # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' # vyos@vyos:~$ # # # "after": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "before": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 100 # }, # { # "afi": "ipv6", # "ifmtu": 33, # "passive": true # } # ], # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], # "changed": true, # "commands": [ # "delete interfaces ethernet eth0 ip ospf", # "delete interfaces ethernet eth0 ipv6 ospfv3" # ], # # Using parsed: # parsed.cfg: # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth0 ip ospf cost '50' # set interfaces ethernet eth0 ip ospf priority '26' # set interfaces ethernet eth0 ipv6 ospfv3 instance-id '33' # set interfaces ethernet eth0 ipv6 ospfv3 'mtu-ignore' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' # - name: parse configs vyos.vyos.vyos_ospf_interfaces: running_config: "{{ lookup('file', './parsed.cfg') }}" state: parsed # Module Execution: # ---------------- # "parsed": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "cost": 50, # "priority": 26 # }, # { # "afi": "ipv6", # "instance": "33", # "mtu_ignore": true # } # ], # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # } # ] # Using rendered: # -------------- - name: Render vyos.vyos.vyos_ospf_interfaces: config: - name: "eth1" address_family: - afi: "ipv4" transmit_delay: 50 priority: 26 network: "point-to-point" - afi: "ipv6" dead_interval: 39 - name: "bond2" address_family: - afi: "ipv4" transmit_delay: 45 bandwidth: 70 authentication: md5_key: key_id: 10 key: "1111111111232345" - afi: "ipv6" passive: True state: rendered # Module Execution: # ---------------- # "rendered": [ # "set interfaces ethernet eth1 ip ospf transmit-delay 50", # "set interfaces ethernet eth1 ip ospf priority 26", # "set interfaces ethernet eth1 ip ospf network point-to-point", # "set interfaces ethernet eth1 ipv6 ospfv3 dead-interval 39", # "set interfaces bonding bond2 ip ospf transmit-delay 45", # "set interfaces bonding bond2 ip ospf bandwidth 70", # "set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key 1111111111232345", # "set interfaces bonding bond2 ipv6 ospfv3 passive" # ] # # Using Gathered: # -------------- # Native Config: # vyos@vyos:~$ show configuration commands | match "ospf" # set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345' # set interfaces bonding bond2 ip ospf bandwidth '70' # set interfaces bonding bond2 ip ospf transmit-delay '45' # set interfaces bonding bond2 ipv6 ospfv3 'passive' # set interfaces ethernet eth1 ip ospf network 'point-to-point' # set interfaces ethernet eth1 ip ospf priority '26' # set interfaces ethernet eth1 ip ospf transmit-delay '50' # set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39' # vyos@vyos:~$ - name: gather configs vyos.vyos.vyos_ospf_interfaces: state: gathered # Module Execution: # ----------------- # "gathered": [ # { # "address_family": [ # { # "afi": "ipv4", # "authentication": { # "md5_key": { # "key": "1111111111232345", # "key_id": 10 # } # }, # "bandwidth": 70, # "transmit_delay": 45 # }, # { # "afi": "ipv6", # "passive": true # } # ], # "name": "bond2" # }, # { # "name": "eth0" # }, # { # "address_family": [ # { # "afi": "ipv4", # "network": "point-to-point", # "priority": 26, # "transmit_delay": 50 # }, # { # "afi": "ipv6", # "dead_interval": 39 # } # ], # "name": "eth1" # }, # { # "name": "eth2" # }, # { # "name": "eth3" # } # ], """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospf_interfaces.ospf_interfaces import ( Ospf_interfacesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospf_interfaces.ospf_interfaces import ( Ospf_interfaces, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=Ospf_interfacesArgs.argument_spec, mutually_exclusive=[], required_if=[], supports_check_mode=False, ) result = Ospf_interfaces(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ospfv2.py b/plugins/modules/vyos_ospfv2.py index 43b0675..863bc6f 100644 --- a/plugins/modules/vyos_ospfv2.py +++ b/plugins/modules/vyos_ospfv2.py @@ -1,1818 +1,1819 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_ospfv2 """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_ospfv2 short_description: OSPFv2 resource module description: This resource module configures and manages attributes of OSPFv2 routes on VyOS network devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A provided OSPFv2 route configuration. type: dict suboptions: areas: description: OSPFv2 area. type: list elements: dict suboptions: area_id: description: OSPFv2 area identity. type: str area_type: description: Area type. type: dict suboptions: normal: description: Normal OSPFv2 area. type: bool nssa: description: NSSA OSPFv2 area. type: dict suboptions: set: description: Enabling NSSA. type: bool default_cost: description: Summary-default cost of NSSA area. type: int no_summary: description: Do not inject inter-area routes into stub. type: bool translate: description: NSSA-ABR. type: str choices: [always, candidate, never] stub: description: Stub OSPFv2 area. type: dict suboptions: set: description: Enabling stub. type: bool default_cost: description: Summary-default cost of stub area. type: int no_summary: description: Do not inject inter-area routes into stub. type: bool authentication: description: OSPFv2 area authentication type. type: str choices: [plaintext-password, md5] network: description: OSPFv2 network. type: list elements: dict suboptions: address: required: true description: OSPFv2 IPv4 network address. type: str range: description: Summarize routes matching prefix (border routers only). type: list elements: dict suboptions: address: description: border router IPv4 address. type: str cost: description: Metric for this range. type: int not_advertise: description: Don't advertise this range. type: bool substitute: description: Announce area range (IPv4 address) as another prefix. type: str shortcut: description: Area's shortcut mode. type: str choices: [default, disable, enable] virtual_link: description: Virtual link address. type: list elements: dict suboptions: address: description: virtual link address. type: str authentication: description: OSPFv2 area authentication type. type: dict suboptions: md5: description: MD5 key id based authentication. type: list elements: dict suboptions: key_id: description: MD5 key id. type: int md5_key: description: MD5 key. type: str plaintext_password: description: Plain text password. type: str dead_interval: description: Interval after which a neighbor is declared dead. type: int hello_interval: description: Interval between hello packets. type: int retransmit_interval: description: Interval between retransmitting lost link state advertisements. type: int transmit_delay: description: Link state transmit delay. type: int log_adjacency_changes: description: Log changes in adjacency state. type: str choices: [detail] max_metric: description: OSPFv2 maximum/infinite-distance metric. type: dict suboptions: router_lsa: description: Advertise own Router-LSA with infinite distance (stub router). type: dict suboptions: administrative: description: Administratively apply, for an indefinite period. type: bool on_shutdown: description: Time to advertise self as stub-router. type: int on_startup: description: Time to advertise self as stub-router type: int auto_cost: description: Calculate OSPFv2 interface cost according to bandwidth. type: dict suboptions: reference_bandwidth: description: Reference bandwidth cost in Mbits/sec. type: int default_information: description: Control distribution of default information. type: dict suboptions: originate: description: Distribute a default route. type: dict suboptions: always: description: Always advertise default route. type: bool metric: description: OSPFv2 default metric. type: int metric_type: description: OSPFv2 Metric types for default routes. type: int route_map: description: Route map references. type: str default_metric: description: Metric of redistributed routes type: int distance: description: Administrative distance. type: dict suboptions: global: description: Global OSPFv2 administrative distance. type: int ospf: description: OSPFv2 administrative distance. type: dict suboptions: external: description: Distance for external routes. type: int inter_area: description: Distance for inter-area routes. type: int intra_area: description: Distance for intra-area routes. type: int mpls_te: description: MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters. type: dict suboptions: enabled: description: Enable MPLS-TE functionality. type: bool router_address: description: Stable IP address of the advertising router. type: str neighbor: description: Neighbor IP address. type: list elements: dict suboptions: neighbor_id: description: Identity (number/IP address) of neighbor. type: str poll_interval: description: Seconds between dead neighbor polling interval. type: int priority: description: Neighbor priority. type: int parameters: description: OSPFv2 specific parameters. type: dict suboptions: abr_type: description: OSPFv2 ABR Type. type: str choices: [cisco, ibm, shortcut, standard] opaque_lsa: description: Enable the Opaque-LSA capability (rfc2370). type: bool rfc1583_compatibility: description: Enable rfc1583 criteria for handling AS external routes. type: bool router_id: description: Override the default router identifier. type: str passive_interface: description: Suppress routing updates on an interface. type: list elements: str passive_interface_exclude: description: Interface to exclude when using passive-interface default. type: list elements: str redistribute: description: Redistribute information from another routing protocol. type: list elements: dict suboptions: route_type: description: Route type to redistribute. type: str choices: [bgp, connected, kernel, rip, static] metric: description: Metric for redistribution routes. type: int metric_type: description: OSPFv2 Metric types. type: int route_map: description: Route map references. type: str route_map: description: Filter routes installed in local route map. type: list elements: str timers: description: Adjust routing timers. type: dict suboptions: refresh: description: Adjust refresh parameters. type: dict suboptions: timers: description: refresh timer. type: int throttle: description: Throttling adaptive timers. type: dict suboptions: spf: description: OSPFv2 SPF timers. type: dict suboptions: delay: description: Delay (msec) from first change received till SPF calculation. type: int initial_holdtime: description: Initial hold time(msec) between consecutive SPF calculations. type: int max_holdtime: description: maximum hold time (sec). type: int running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep ospf). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep ospf # # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_ospfv2: config: log_adjacency_changes: detail max_metric: router_lsa: administrative: true on_shutdown: 10 on_startup: 10 default_information: originate: always: true metric: 10 metric_type: 2 route_map: ingress mpls_te: enabled: true router_address: 192.0.11.11 auto_cost: reference_bandwidth: 2 neighbor: - neighbor_id: 192.0.11.12 poll_interval: 10 priority: 2 redistribute: - route_type: bgp metric: 10 metric_type: 2 passive_interface: - eth1 - eth2 parameters: router_id: 192.0.1.1 opaque_lsa: true rfc1583_compatibility: true abr_type: cisco areas: - area_id: '2' area_type: normal: true authentication: plaintext-password shortcut: enable - area_id: '3' area_type: nssa: set: true - area_id: '4' area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 range: - address: 192.0.3.0/24 cost: 10 - address: 192.0.4.0/24 cost: 12 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": {} # # "commands": [ # "set protocols ospf mpls-te enable", # "set protocols ospf mpls-te router-address '192.0.11.11'", # "set protocols ospf redistribute bgp", # "set protocols ospf redistribute bgp metric-type 2", # "set protocols ospf redistribute bgp metric 10", # "set protocols ospf default-information originate metric-type 2", # "set protocols ospf default-information originate always", # "set protocols ospf default-information originate metric 10", # "set protocols ospf default-information originate route-map ingress", # "set protocols ospf auto-cost reference-bandwidth '2'", # "set protocols ospf parameters router-id '192.0.1.1'", # "set protocols ospf parameters opaque-lsa", # "set protocols ospf parameters abr-type 'cisco'", # "set protocols ospf parameters rfc1583-compatibility", # "set protocols ospf passive-interface eth1", # "set protocols ospf passive-interface eth2", # "set protocols ospf max-metric router-lsa on-shutdown 10", # "set protocols ospf max-metric router-lsa administrative", # "set protocols ospf max-metric router-lsa on-startup 10", # "set protocols ospf log-adjacency-changes 'detail'", # "set protocols ospf neighbor 192.0.11.12 priority 2", # "set protocols ospf neighbor 192.0.11.12 poll-interval 10", # "set protocols ospf neighbor 192.0.11.12", # "set protocols ospf area '2'", # "set protocols ospf area 2 authentication plaintext-password", # "set protocols ospf area 2 shortcut enable", # "set protocols ospf area 2 area-type normal", # "set protocols ospf area '3'", # "set protocols ospf area 3 area-type nssa", # "set protocols ospf area 4 range 192.0.3.0/24 cost 10", # "set protocols ospf area 4 range 192.0.3.0/24", # "set protocols ospf area 4 range 192.0.4.0/24 cost 12", # "set protocols ospf area 4 range 192.0.4.0/24", # "set protocols ospf area 4 area-type stub default-cost 20", # "set protocols ospf area '4'", # "set protocols ospf area 4 network 192.0.2.0/24" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.2.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf passive-interface 'eth2' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep ospf # # - name: Merge the provided configuration to update existing running configuration vyos.vyos.vyos_ospfv2: config: areas: - area_id: '2' area_type: normal: true authentication: plaintext-password shortcut: enable - area_id: '3' area_type: nssa: set: false - area_id: '4' area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 - address: 192.0.22.0/24 - address: 192.0.32.0/24 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # "before": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.2.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # # "commands": [ # "delete protocols ospf area 4 area-type stub", # "set protocols ospf area 4 network 192.0.22.0/24" # "set protocols ospf area 4 network 192.0.32.0/24" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # }, # "network": [ # { # "address": "192.0.2.0/24" # }, # { # "address": "192.0.22.0/24" # }, # { # "address": "192.0.32.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 network '192.0.22.0/24' # set protocols ospf area 4 network '192.0.32.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf passive-interface 'eth2' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using replaced # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf passive-interface 'eth2' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # - name: Replace ospfv2 routes attributes configuration. vyos.vyos.vyos_ospfv2: config: log_adjacency_changes: detail max_metric: router_lsa: administrative: true on_shutdown: 10 on_startup: 10 default_information: originate: always: true metric: 10 metric_type: 2 route_map: ingress mpls_te: enabled: true router_address: 192.0.22.22 auto_cost: reference_bandwidth: 2 neighbor: - neighbor_id: 192.0.11.12 poll_interval: 10 priority: 2 redistribute: - route_type: bgp metric: 10 metric_type: 2 passive_interface: - eth1 parameters: router_id: 192.0.1.1 opaque_lsa: true rfc1583_compatibility: true abr_type: cisco areas: - area_id: '2' area_type: normal: true authentication: plaintext-password shortcut: enable - area_id: '4' area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 - address: 192.0.12.0/24 - address: 192.0.22.0/24 - address: 192.0.32.0/24 range: - address: 192.0.42.0/24 cost: 10 state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.2.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # # "commands": [ # "delete protocols ospf passive-interface eth2", # "delete protocols ospf area 3", # "delete protocols ospf area 4 range 192.0.3.0/24 cost", # "delete protocols ospf area 4 range 192.0.3.0/24", # "delete protocols ospf area 4 range 192.0.4.0/24 cost", # "delete protocols ospf area 4 range 192.0.4.0/24", # "set protocols ospf mpls-te router-address '192.0.22.22'", # "set protocols ospf area 4 range 192.0.42.0/24 cost 10", # "set protocols ospf area 4 range 192.0.42.0/24", # "set protocols ospf area 4 network 192.0.12.0/24", # "set protocols ospf area 4 network 192.0.22.0/24", # "set protocols ospf area 4 network 192.0.32.0/24" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.12.0/24" # }, # { # "address": "192.0.2.0/24" # }, # { # "address": "192.0.22.0/24" # }, # { # "address": "192.0.32.0/24" # } # ], # "range": [ # { # "address": "192.0.42.0/24", # "cost": 10 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.22.22" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 network '192.0.12.0/24' # set protocols ospf area 4 network '192.0.22.0/24' # set protocols ospf area 4 network '192.0.32.0/24' # set protocols ospf area 4 range 192.0.42.0/24 cost '10' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.22.22' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_ospfv2: config: log_adjacency_changes: detail max_metric: router_lsa: administrative: true on_shutdown: 10 on_startup: 10 default_information: originate: always: true metric: 10 metric_type: 2 route_map: ingress mpls_te: enabled: true router_address: 192.0.11.11 auto_cost: reference_bandwidth: 2 neighbor: - neighbor_id: 192.0.11.12 poll_interval: 10 priority: 2 redistribute: - route_type: bgp metric: 10 metric_type: 2 passive_interface: - eth1 - eth2 parameters: router_id: 192.0.1.1 opaque_lsa: true rfc1583_compatibility: true abr_type: cisco areas: - area_id: '2' area_type: normal: true authentication: plaintext-password shortcut: enable - area_id: '3' area_type: nssa: set: true - area_id: '4' area_type: stub: default_cost: 20 network: - address: 192.0.2.0/24 range: - address: 192.0.3.0/24 cost: 10 - address: 192.0.4.0/24 cost: 12 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # [ # "set protocols ospf mpls-te enable", # "set protocols ospf mpls-te router-address '192.0.11.11'", # "set protocols ospf redistribute bgp", # "set protocols ospf redistribute bgp metric-type 2", # "set protocols ospf redistribute bgp metric 10", # "set protocols ospf default-information originate metric-type 2", # "set protocols ospf default-information originate always", # "set protocols ospf default-information originate metric 10", # "set protocols ospf default-information originate route-map ingress", # "set protocols ospf auto-cost reference-bandwidth '2'", # "set protocols ospf parameters router-id '192.0.1.1'", # "set protocols ospf parameters opaque-lsa", # "set protocols ospf parameters abr-type 'cisco'", # "set protocols ospf parameters rfc1583-compatibility", # "set protocols ospf passive-interface eth1", # "set protocols ospf passive-interface eth2", # "set protocols ospf max-metric router-lsa on-shutdown 10", # "set protocols ospf max-metric router-lsa administrative", # "set protocols ospf max-metric router-lsa on-startup 10", # "set protocols ospf log-adjacency-changes 'detail'", # "set protocols ospf neighbor 192.0.11.12 priority 2", # "set protocols ospf neighbor 192.0.11.12 poll-interval 10", # "set protocols ospf neighbor 192.0.11.12", # "set protocols ospf area '2'", # "set protocols ospf area 2 authentication plaintext-password", # "set protocols ospf area 2 shortcut enable", # "set protocols ospf area 2 area-type normal", # "set protocols ospf area '3'", # "set protocols ospf area 3 area-type nssa", # "set protocols ospf area 4 range 192.0.3.0/24 cost 10", # "set protocols ospf area 4 range 192.0.3.0/24", # "set protocols ospf area 4 range 192.0.4.0/24 cost 12", # "set protocols ospf area 4 range 192.0.4.0/24", # "set protocols ospf area 4 area-type stub default-cost 20", # "set protocols ospf area '4'", # "set protocols ospf area 4 network 192.0.2.0/24" # ] # Using parsed # # - name: Parse the commands for provided structured configuration vyos.vyos.vyos_ospfv2: running_config: "set protocols ospf area 2 area-type 'normal' set protocols ospf area 2 authentication 'plaintext-password' set protocols ospf area 2 shortcut 'enable' set protocols ospf area 3 area-type 'nssa' set protocols ospf area 4 area-type stub default-cost '20' set protocols ospf area 4 network '192.0.2.0/24' set protocols ospf area 4 range 192.0.3.0/24 cost '10' set protocols ospf area 4 range 192.0.4.0/24 cost '12' set protocols ospf auto-cost reference-bandwidth '2' set protocols ospf default-information originate 'always' set protocols ospf default-information originate metric '10' set protocols ospf default-information originate metric-type '2' set protocols ospf default-information originate route-map 'ingress' set protocols ospf log-adjacency-changes 'detail' set protocols ospf max-metric router-lsa 'administrative' set protocols ospf max-metric router-lsa on-shutdown '10' set protocols ospf max-metric router-lsa on-startup '10' set protocols ospf mpls-te 'enable' set protocols ospf mpls-te router-address '192.0.11.11' set protocols ospf neighbor 192.0.11.12 poll-interval '10' set protocols ospf neighbor 192.0.11.12 priority '2' set protocols ospf parameters abr-type 'cisco' set protocols ospf parameters 'opaque-lsa' set protocols ospf parameters 'rfc1583-compatibility' set protocols ospf parameters router-id '192.0.1.1' set protocols ospf passive-interface 'eth1' set protocols ospf passive-interface 'eth2' set protocols ospf redistribute bgp metric '10' set protocols ospf redistribute bgp metric-type '2'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.2.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # } # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf passive-interface 'eth2' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # - name: Gather ospfv2 routes config with provided configurations vyos.vyos.vyos_ospfv2: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.2.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf passive-interface 'eth2' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # Using deleted # # Before state # ------------- # # vyos@192# run show configuration commands | grep ospf # set protocols ospf area 2 area-type 'normal' # set protocols ospf area 2 authentication 'plaintext-password' # set protocols ospf area 2 shortcut 'enable' # set protocols ospf area 3 area-type 'nssa' # set protocols ospf area 4 area-type stub default-cost '20' # set protocols ospf area 4 network '192.0.2.0/24' # set protocols ospf area 4 range 192.0.3.0/24 cost '10' # set protocols ospf area 4 range 192.0.4.0/24 cost '12' # set protocols ospf auto-cost reference-bandwidth '2' # set protocols ospf default-information originate 'always' # set protocols ospf default-information originate metric '10' # set protocols ospf default-information originate metric-type '2' # set protocols ospf default-information originate route-map 'ingress' # set protocols ospf log-adjacency-changes 'detail' # set protocols ospf max-metric router-lsa 'administrative' # set protocols ospf max-metric router-lsa on-shutdown '10' # set protocols ospf max-metric router-lsa on-startup '10' # set protocols ospf mpls-te 'enable' # set protocols ospf mpls-te router-address '192.0.11.11' # set protocols ospf neighbor 192.0.11.12 poll-interval '10' # set protocols ospf neighbor 192.0.11.12 priority '2' # set protocols ospf parameters abr-type 'cisco' # set protocols ospf parameters 'opaque-lsa' # set protocols ospf parameters 'rfc1583-compatibility' # set protocols ospf parameters router-id '192.0.1.1' # set protocols ospf passive-interface 'eth1' # set protocols ospf passive-interface 'eth2' # set protocols ospf redistribute bgp metric '10' # set protocols ospf redistribute bgp metric-type '2' # - name: Delete attributes of ospfv2 routes. vyos.vyos.vyos_ospfv2: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { # "areas": [ # { # "area_id": "2", # "area_type": { # "normal": true # }, # "authentication": "plaintext-password", # "shortcut": "enable" # }, # { # "area_id": "3", # "area_type": { # "nssa": { # "set": true # } # } # }, # { # "area_id": "4", # "area_type": { # "stub": { # "default_cost": 20, # "set": true # } # }, # "network": [ # { # "address": "192.0.2.0/24" # } # ], # "range": [ # { # "address": "192.0.3.0/24", # "cost": 10 # }, # { # "address": "192.0.4.0/24", # "cost": 12 # } # ] # } # ], # "auto_cost": { # "reference_bandwidth": 2 # }, # "default_information": { # "originate": { # "always": true, # "metric": 10, # "metric_type": 2, # "route_map": "ingress" # } # }, # "log_adjacency_changes": "detail", # "max_metric": { # "router_lsa": { # "administrative": true, # "on_shutdown": 10, # "on_startup": 10 # } # }, # "mpls_te": { # "enabled": true, # "router_address": "192.0.11.11" # }, # "neighbor": [ # { # "neighbor_id": "192.0.11.12", # "poll_interval": 10, # "priority": 2 # } # ], # "parameters": { # "abr_type": "cisco", # "opaque_lsa": true, # "rfc1583_compatibility": true, # "router_id": "192.0.1.1" # }, # "passive_interface": [ # "eth2", # "eth1" # ], # "redistribute": [ # { # "metric": 10, # "metric_type": 2, # "route_type": "bgp" # } # ] # } # "commands": [ # "delete protocols ospf" # ] # # "after": {} # After state # ------------ # vyos@192# run show configuration commands | grep ospf # """ RETURN = """ before: description: The configuration prior to the model invocation. returned: always type: dict sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed type: dict sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set protocols ospf parameters router-id 192.0.1.1" - "set protocols ospf passive-interface 'eth1'" """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( Ospfv2Args, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import ( Ospfv2, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Ospfv2Args.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Ospfv2(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ospfv3.py b/plugins/modules/vyos_ospfv3.py index 8942cef..4ff6106 100644 --- a/plugins/modules/vyos_ospfv3.py +++ b/plugins/modules/vyos_ospfv3.py @@ -1,667 +1,668 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_ospfv3 """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_ospfv3 short_description: OSPFV3 resource module description: This resource module configures and manages attributes of OSPFv3 routes on VyOS network devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: A provided OSPFv3 route configuration. type: dict suboptions: areas: description: OSPFv3 area. type: list elements: dict suboptions: area_id: description: OSPFv3 Area name/identity. type: str export_list: description: Name of export-list. type: str import_list: description: Name of import-list. type: str range: description: Summarize routes matching prefix (border routers only). type: list elements: dict suboptions: address: description: border router IPv4 address. type: str advertise: description: Advertise this range. type: bool not_advertise: description: Don't advertise this range. type: bool parameters: description: OSPFv3 specific parameters. type: dict suboptions: router_id: description: Override the default router identifier. type: str redistribute: description: Redistribute information from another routing protocol. type: list elements: dict suboptions: route_type: description: Route type to redistribute. type: str choices: [bgp, connected, kernel, ripng, static] route_map: description: Route map references. type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep ospfv3). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - merged - replaced - deleted - parsed - gathered - rendered default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep ospfv3 # # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_ospfv3: config: redistribute: - route_type: bgp parameters: router_id: 192.0.2.10 areas: - area_id: '2' export_list: export1 import_list: import1 range: - address: 2001:db10::/32 - address: 2001:db20::/32 - address: 2001:db30::/32 - area_id: '3' range: - address: 2001:db40::/32 state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": {} # # "commands": [ # "set protocols ospfv3 redistribute bgp", # "set protocols ospfv3 parameters router-id '192.0.2.10'", # "set protocols ospfv3 area 2 range 2001:db10::/32", # "set protocols ospfv3 area 2 range 2001:db20::/32", # "set protocols ospfv3 area 2 range 2001:db30::/32", # "set protocols ospfv3 area '2'", # "set protocols ospfv3 area 2 export-list export1", # "set protocols ospfv3 area 2 import-list import1", # "set protocols ospfv3 area '3'", # "set protocols ospfv3 area 3 range 2001:db40::/32" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # Using replaced # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # - name: Replace ospfv3 routes attributes configuration. vyos.vyos.vyos_ospfv3: config: redistribute: - route_type: bgp parameters: router_id: 192.0.2.10 areas: - area_id: '2' export_list: export1 import_list: import1 range: - address: 2001:db10::/32 - address: 2001:db30::/32 - address: 2001:db50::/32 - area_id: '4' range: - address: 2001:db60::/32 state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # "commands": [ # "delete protocols ospfv3 area 2 range 2001:db20::/32", # "delete protocols ospfv3 area 3", # "set protocols ospfv3 area 2 range 2001:db50::/32", # "set protocols ospfv3 area '4'", # "set protocols ospfv3 area 4 range 2001:db60::/32" # ] # # "after": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db30::/32" # }, # { # "address": "2001:db50::/32" # } # ] # }, # { # "area_id": "4", # "range": [ # { # "address": "2001:db60::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 2 range '2001:db50::/32' # set protocols ospfv3 area 4 range '2001:db60::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_ospfv3: config: redistribute: - route_type: bgp parameters: router_id: 192.0.2.10 areas: - area_id: '2' export_list: export1 import_list: import1 range: - address: 2001:db10::/32 - address: 2001:db20::/32 - address: 2001:db30::/32 - area_id: '3' range: - address: 2001:db40::/32 state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # [ # "set protocols ospfv3 redistribute bgp", # "set protocols ospfv3 parameters router-id '192.0.2.10'", # "set protocols ospfv3 area 2 range 2001:db10::/32", # "set protocols ospfv3 area 2 range 2001:db20::/32", # "set protocols ospfv3 area 2 range 2001:db30::/32", # "set protocols ospfv3 area '2'", # "set protocols ospfv3 area 2 export-list export1", # "set protocols ospfv3 area 2 import-list import1", # "set protocols ospfv3 area '3'", # "set protocols ospfv3 area 3 range 2001:db40::/32" # ] # Using parsed # # - name: Parse the commands to provide structured configuration. vyos.vyos.vyos_ospfv3: running_config: "set protocols ospfv3 area 2 export-list 'export1' set protocols ospfv3 area 2 import-list 'import1' set protocols ospfv3 area 2 range '2001:db10::/32' set protocols ospfv3 area 2 range '2001:db20::/32' set protocols ospfv3 area 2 range '2001:db30::/32' set protocols ospfv3 area 3 range '2001:db40::/32' set protocols ospfv3 parameters router-id '192.0.2.10' set protocols ospfv3 redistribute 'bgp'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # - name: Gather ospfv3 routes config with provided configurations vyos.vyos.vyos_ospfv3: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # Using deleted # # Before state # ------------- # # vyos@192# run show configuration commands | grep ospfv3 # set protocols ospfv3 area 2 export-list 'export1' # set protocols ospfv3 area 2 import-list 'import1' # set protocols ospfv3 area 2 range '2001:db10::/32' # set protocols ospfv3 area 2 range '2001:db20::/32' # set protocols ospfv3 area 2 range '2001:db30::/32' # set protocols ospfv3 area 3 range '2001:db40::/32' # set protocols ospfv3 parameters router-id '192.0.2.10' # set protocols ospfv3 redistribute 'bgp' # - name: Delete attributes of ospfv3 routes. vyos.vyos.vyos_ospfv3: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { # "areas": [ # { # "area_id": "2", # "export_list": "export1", # "import_list": "import1", # "range": [ # { # "address": "2001:db10::/32" # }, # { # "address": "2001:db20::/32" # }, # { # "address": "2001:db30::/32" # } # ] # }, # { # "area_id": "3", # "range": [ # { # "address": "2001:db40::/32" # } # ] # } # ], # "parameters": { # "router_id": "192.0.2.10" # }, # "redistribute": [ # { # "route_type": "bgp" # } # ] # } # "commands": [ # "delete protocols ospfv3" # ] # # "after": {} # After state # ------------ # vyos@192# run show configuration commands | grep ospfv3 """ RETURN = """ before: description: The configuration prior to the model invocation. returned: always type: dict sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration model invocation. returned: when changed type: dict sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set protocols ospf parameters router-id 192.0.1.1" - "set protocols ospfv3 area 2 range '2001:db10::/32'" """ from ansible.module_utils.basic import AnsibleModule + from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv3.ospfv3 import ( Ospfv3Args, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import ( Ospfv3, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Ospfv3Args.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Ospfv3(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_ping.py b/plugins/modules/vyos_ping.py index 7934583..8c0330d 100644 --- a/plugins/modules/vyos_ping.py +++ b/plugins/modules/vyos_ping.py @@ -1,256 +1,252 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # (c) 2017, Ansible by Red Hat, inc # # This file is part of Ansible by Red Hat # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_ping short_description: Tests reachability using ping from VyOS network devices description: - Tests reachability using ping from a VyOS device to a remote destination. - Tested against VyOS 1.1.8 (helium) - For a general purpose network module, see the M(ansible.netcommon.net_ping) module. - For Windows targets, use the M(ansible.windows.win_ping) module instead. - For targets running Python, use the M(ansible.builtin.ping) module instead. version_added: 1.0.0 author: - Nilashish Chakraborty (@NilashishC) options: dest: description: - The IP Address or hostname (resolvable by the device) of the remote node. required: true type: str count: description: - Number of packets to send to check reachability. type: int default: 5 source: description: - The source interface or IP Address to use while sending the ping packet(s). type: str ttl: description: - The time-to-live value for the ICMP packet(s). type: int size: description: - Determines the size (in bytes) of the ping packet(s). type: int interval: description: - Determines the interval (in seconds) between consecutive pings. type: int state: description: - Determines if the expected result is success or fail. type: str choices: - absent - present default: present notes: - Tested against VyOS 1.1.8 (helium). - For a general purpose network module, see the M(ansible.netcommon.net_ping) module. - For Windows targets, use the M(ansible.windows.win_ping) module instead. - For targets running Python, use the M(ansible.builtin.ping) module instead. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). extends_documentation_fragment: - vyos.vyos.vyos """ EXAMPLES = """ - name: Test reachability to 10.10.10.10 vyos.vyos.vyos_ping: dest: 10.10.10.10 - name: Test reachability to 10.20.20.20 using source and ttl set vyos.vyos.vyos_ping: dest: 10.20.20.20 source: eth0 ttl: 128 - name: Test reachability to 10.30.30.30 using interval vyos.vyos.vyos_ping: dest: 10.30.30.30 interval: 3 state: absent - name: Test reachability to 10.40.40.40 setting count and source vyos.vyos.vyos_ping: dest: 10.40.40.40 source: eth1 count: 20 size: 512 """ RETURN = """ commands: description: List of commands sent. returned: always type: list sample: ["ping 10.8.38.44 count 10 interface eth0 ttl 128"] packet_loss: description: Percentage of packets lost. returned: always type: str sample: "0%" packets_rx: description: Packets successfully received. returned: always type: int sample: 20 packets_tx: description: Packets successfully transmitted. returned: always type: int sample: 20 rtt: description: The round trip time (RTT) stats. returned: when ping succeeds type: dict sample: {"avg": 2, "max": 8, "min": 1, "mdev": 24} """ -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - run_commands, -) import re +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import run_commands + def main(): """main entry point for module execution""" argument_spec = dict( count=dict(type="int", default=5), dest=dict(type="str", required=True), source=dict(type="str"), ttl=dict(type="int"), size=dict(type="int"), interval=dict(type="int"), - state=dict( - type="str", choices=["absent", "present"], default="present" - ), + state=dict(type="str", choices=["absent", "present"], default="present"), ) module = AnsibleModule(argument_spec=argument_spec) count = module.params["count"] dest = module.params["dest"] source = module.params["source"] size = module.params["size"] ttl = module.params["ttl"] interval = module.params["interval"] warnings = list() results = {} if warnings: results["warnings"] = warnings - results["commands"] = [ - build_ping(dest, count, size, interval, source, ttl) - ] + results["commands"] = [build_ping(dest, count, size, interval, source, ttl)] ping_results = run_commands(module, commands=results["commands"]) ping_results_list = ping_results[0].split("\n") rtt_info, rate_info = None, None for line in ping_results_list: if line.startswith("rtt"): rtt_info = line if line.startswith("%s packets transmitted" % count): rate_info = line if rtt_info: rtt = parse_rtt(rtt_info) for k, v in rtt.items(): if rtt[k] is not None: rtt[k] = int(v) results["rtt"] = rtt pkt_loss, rx, tx = parse_rate(rate_info) results["packet_loss"] = str(pkt_loss) + "%" results["packets_rx"] = int(rx) results["packets_tx"] = int(tx) validate_results(module, pkt_loss, results) module.exit_json(**results) def build_ping(dest, count, size=None, interval=None, source=None, ttl=None): cmd = "ping {0} count {1}".format(dest, str(count)) if source: cmd += " interface {0}".format(source) if ttl: cmd += " ttl {0}".format(str(ttl)) if size: cmd += " size {0}".format(str(size)) if interval: cmd += " interval {0}".format(str(interval)) return cmd def parse_rate(rate_info): rate_re = re.compile( r"(?P\d+) (?:\w+) (?:\w+), (?P\d+) (?:\w+), (?P\d+)% (?:\w+) (?:\w+), (?:\w+) (?P