[stable-2.17] ansible-test - Remove VyOS tests and support files (#83650) (#83651)

The VyOS remote image required for running the tests is no longer functional.
(cherry picked from commit 79f819dc54)
pull/83677/head
Matt Clay 4 months ago committed by GitHub
parent f02fd32fe0
commit 03446c7f23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -193,8 +193,6 @@ stages:
targets:
- name: IOS Python
test: ios/csr1000v/
- name: VyOS Python
test: vyos/1.1.8/
- stage: Summary
condition: succeededOrFailed()
dependsOn:

@ -0,0 +1,2 @@
minor_changes:
- ansible-test - Removed the ``vyos/1.1.8`` network remote as it is no longer functional.

@ -1,2 +0,0 @@
shippable/vyos/incidental
network/vyos

@ -1,26 +0,0 @@
---
- name: collect all cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
register: test_cases
delegate_to: localhost
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test case (connection=ansible.netcommon.network_cli)
include_tasks: "file={{ test_case_to_run }}"
vars:
ansible_connection: ansible.netcommon.network_cli
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run
- name: run test case (connection=local)
include_tasks: "file={{ test_case_to_run }}"
vars:
ansible_connection: local
with_first_found: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

@ -1,18 +0,0 @@
---
- name: collect all cli_config test cases
find:
paths: "{{ role_path }}/tests/cli_config"
patterns: "{{ testcase }}.yaml"
register: test_cases
delegate_to: localhost
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test case (connection=ansible.netcommon.network_cli)
include_tasks: "file={{ test_case_to_run }}"
vars:
ansible_connection: ansible.netcommon.network_cli
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

@ -1,3 +0,0 @@
---
- {import_tasks: cli.yaml, tags: ['cli']}
- {import_tasks: cli_config.yaml, tags: ['cli_config']}

@ -1,113 +0,0 @@
---
- debug: msg="START vyos/backup.yaml on connection={{ ansible_connection }}"
- name: collect any backup files
find:
paths: "{{ role_path }}/backup"
pattern: "{{ inventory_hostname_short }}_config*"
register: backup_files
connection: local
- name: delete backup files
file:
path: "{{ item.path }}"
state: absent
with_items: "{{backup_files.files|default([])}}"
- name: take configure backup
vyos.vyos.vyos_config:
backup: true
register: result
- assert:
that:
- "result.changed == true"
- name: collect any backup files
find:
paths: "{{ role_path }}/backup"
pattern: "{{ inventory_hostname_short }}_config*"
register: backup_files
connection: local
- assert:
that:
- "backup_files.files is defined"
- name: delete configurable backup file path
file:
path: "{{ item }}"
state: absent
with_items:
- "{{ role_path }}/backup_test_dir/"
- "{{ role_path }}/backup/backup.cfg"
- name: take configuration backup in custom filename and directory path
vyos.vyos.vyos_config:
backup: true
backup_options:
filename: backup.cfg
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: check if the backup file-1 exist
find:
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}/backup.cfg"
register: backup_file
connection: local
- assert:
that:
- "backup_file.files is defined"
- name: take configuration backup in custom filename
vyos.vyos.vyos_config:
backup: true
backup_options:
filename: backup.cfg
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: check if the backup file-2 exist
find:
paths: "{{ role_path }}/backup/backup.cfg"
register: backup_file
connection: local
- assert:
that:
- "backup_file.files is defined"
- name: take configuration backup in custom path and default filename
vyos.vyos.vyos_config:
backup: true
backup_options:
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: check if the backup file-3 exist
find:
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
pattern: "{{ inventory_hostname_short }}_config*"
register: backup_file
connection: local
- assert:
that:
- "backup_file.files is defined"
- debug: msg="END vyos/backup.yaml on connection={{ ansible_connection }}"

@ -1,63 +0,0 @@
---
- debug: msg="START cli/config_check.yaml on connection={{ ansible_connection }}"
- name: setup- ensure interface is not present
vyos.vyos.vyos_config:
lines: delete interfaces loopback lo
- name: setup- create interface
vyos.vyos.vyos_config:
lines:
- interfaces
- interfaces loopback lo
- interfaces loopback lo description test
register: result
# note collapsing the duplicate lines doesn't work if
# lines:
# - interfaces loopback lo description test
# - interfaces loopback lo
# - interfaces
- name: Check that multiple duplicate lines collapse into a single commands
assert:
that:
- "result.commands|length == 1"
- name: Check that set is correctly prepended
assert:
that:
- "result.commands[0] == 'set interfaces loopback lo description test'"
- name: configure config_check config command
vyos.vyos.vyos_config:
lines: delete interfaces loopback lo
register: result
- assert:
that:
- "result.changed == true"
- name: check config_check config command idempontent
vyos.vyos.vyos_config:
lines: delete interfaces loopback lo
register: result
- assert:
that:
- "result.changed == false"
- name: check multiple line config filter is working
vyos.vyos.vyos_config:
lines:
- set system login user esa level admin
- set system login user esa authentication encrypted-password '!abc!'
- set system login user vyos level admin
- set system login user vyos authentication encrypted-password 'abc'
register: result
- assert:
that:
- "result.filtered|length == 2"
- debug: msg="END cli/config_check.yaml on connection={{ ansible_connection }}"

@ -1,34 +0,0 @@
---
- debug: msg="START cli/comment.yaml on connection={{ ansible_connection }}"
- name: setup
vyos.vyos.vyos_config:
lines: set system host-name {{ inventory_hostname_short }}
match: none
- name: configure using comment
vyos.vyos.vyos_config:
lines: set system host-name foo
comment: this is a test
register: result
- assert:
that:
- "result.changed == true"
- "'set system host-name foo' in result.commands"
- name: collect system commits
vyos.vyos.vyos_command:
commands: show system commit
register: result
- assert:
that:
- "'this is a test' in result.stdout_lines[0][1]"
- name: teardown
vyos.vyos.vyos_config:
lines: set system host-name {{ inventory_hostname_short }}
match: none
- debug: msg="END cli/comment.yaml on connection={{ ansible_connection }}"

@ -1,54 +0,0 @@
---
- debug: msg="START cli/save.yaml on connection={{ ansible_connection }}"
- name: setup
vyos.vyos.vyos_config:
lines: set system host-name {{ inventory_hostname_short }}
match: none
- name: configure hostaname and save
vyos.vyos.vyos_config:
lines: set system host-name foo
save: true
register: result
- assert:
that:
- "result.changed == true"
- "'set system host-name foo' in result.commands"
- name: configure hostaname and don't save
vyos.vyos.vyos_config:
lines: set system host-name bar
register: result
- assert:
that:
- "result.changed == true"
- "'set system host-name bar' in result.commands"
- name: save config
vyos.vyos.vyos_config:
save: true
register: result
- assert:
that:
- "result.changed == true"
- name: save config again
vyos.vyos.vyos_config:
save: true
register: result
- assert:
that:
- "result.changed == false"
- name: teardown
vyos.vyos.vyos_config:
lines: set system host-name {{ inventory_hostname_short }}
match: none
save: true
- debug: msg="END cli/simple.yaml on connection={{ ansible_connection }}"

@ -1,53 +0,0 @@
---
- debug: msg="START cli/simple.yaml on connection={{ ansible_connection }}"
- name: setup
vyos.vyos.vyos_config:
lines: set system host-name {{ inventory_hostname_short }}
match: none
- name: configure simple config command
vyos.vyos.vyos_config:
lines: set system host-name foo
register: result
- assert:
that:
- "result.changed == true"
- "'set system host-name foo' in result.commands"
- name: check simple config command idempontent
vyos.vyos.vyos_config:
lines: set system host-name foo
register: result
- assert:
that:
- "result.changed == false"
- name: Delete services
vyos.vyos.vyos_config: &del
lines:
- delete service lldp
- delete protocols static
- name: Configuring when commands starts with whitespaces
vyos.vyos.vyos_config:
src: "{{ role_path }}/tests/cli/config.cfg"
register: result
- assert:
that:
- "result.changed == true"
- '"set service lldp" in result.commands'
- '"set protocols static" in result.commands'
- name: Delete services
vyos.vyos.vyos_config: *del
- name: teardown
vyos.vyos.vyos_config:
lines: set system host-name {{ inventory_hostname_short }}
match: none
- debug: msg="END cli/simple.yaml on connection={{ ansible_connection }}"

@ -1,114 +0,0 @@
---
- debug: msg="END cli_config/backup.yaml on connection={{ ansible_connection }}"
- name: delete configurable backup file path
file:
path: "{{ item }}"
state: absent
with_items:
- "{{ role_path }}/backup_test_dir/"
- "{{ role_path }}/backup/backup.cfg"
- name: collect any backup files
find:
paths: "{{ role_path }}/backup"
pattern: "{{ inventory_hostname_short }}_config*"
register: backup_files
connection: local
- name: delete backup files
file:
path: "{{ item.path }}"
state: absent
with_items: "{{backup_files.files|default([])}}"
- name: take config backup
ansible.netcommon.cli_config:
backup: true
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: collect any backup files
find:
paths: "{{ role_path }}/backup"
pattern: "{{ inventory_hostname_short }}_config*"
register: backup_files
connection: local
- assert:
that:
- "backup_files.files is defined"
- name: take configuration backup in custom filename and directory path
ansible.netcommon.cli_config:
backup: true
backup_options:
filename: backup.cfg
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: check if the backup file-1 exist
find:
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}/backup.cfg"
register: backup_file
connection: local
- assert:
that:
- "backup_file.files is defined"
- name: take configuration backup in custom filename
ansible.netcommon.cli_config:
backup: true
backup_options:
filename: backup.cfg
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: check if the backup file-2 exist
find:
paths: "{{ role_path }}/backup/backup.cfg"
register: backup_file
connection: local
- assert:
that:
- "backup_file.files is defined"
- name: take configuration backup in custom path and default filename
ansible.netcommon.cli_config:
backup: true
backup_options:
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
become: true
register: result
- assert:
that:
- "result.changed == true"
- name: check if the backup file-3 exist
find:
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
pattern: "{{ inventory_hostname_short }}_config*"
register: backup_file
connection: local
- assert:
that:
- "backup_file.files is defined"
- debug: msg="END cli_config/backup.yaml on connection={{ ansible_connection }}"

@ -1,28 +0,0 @@
---
- debug: msg="START cli_config/cli_basic.yaml on connection={{ ansible_connection }}"
- name: setup - remove interface description
ansible.netcommon.cli_config: &rm
config: delete interfaces loopback lo description
- name: configure device with config
ansible.netcommon.cli_config: &conf
config: set interfaces loopback lo description 'this is a test'
register: result
- assert:
that:
- "result.changed == true"
- name: Idempotence
ansible.netcommon.cli_config: *conf
register: result
- assert:
that:
- "result.changed == false"
- name: teardown
ansible.netcommon.cli_config: *rm
- debug: msg="END cli_config/cli_basic.yaml on connection={{ ansible_connection }}"

@ -1,30 +0,0 @@
---
- debug: msg="START cli_config/cli_comment.yaml on connection={{ ansible_connection }}"
- name: setup
ansible.netcommon.cli_config: &rm
config: set system host-name {{ inventory_hostname_short }}
- name: configure using comment
ansible.netcommon.cli_config:
config: set system host-name foo
commit_comment: this is a test
register: result
- assert:
that:
- "result.changed == true"
- name: collect system commits
vyos.vyos.vyos_command:
commands: show system commit
register: result
- assert:
that:
- "'this is a test' in result.stdout_lines[0][1]"
- name: teardown
ansible.netcommon.cli_config: *rm
- debug: msg="END cli_config/cli_comment.yaml on connection={{ ansible_connection }}"

@ -1,2 +0,0 @@
shippable/vyos/incidental
network/vyos

@ -1,3 +0,0 @@
---
dependencies:
- incidental_vyos_prepare_tests

@ -1,19 +0,0 @@
---
- name: Collect all cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
use_regex: true
register: test_cases
delegate_to: localhost
- name: Set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: Run test case (connection=ansible.netcommon.network_cli)
include_tasks: "{{ test_case_to_run }}"
vars:
ansible_connection: ansible.netcommon.network_cli
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

@ -1,2 +0,0 @@
---
- {import_tasks: cli.yaml, tags: ['cli']}

@ -1,14 +0,0 @@
---
- name: Setup
ansible.netcommon.cli_config:
config: "{{ lines }}"
vars:
lines: |
set service lldp interface eth1
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 eth2
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

@ -1,10 +0,0 @@
---
- name: Setup
ansible.netcommon.cli_config:
config: "{{ lines }}"
vars:
lines: |
set service lldp interface eth2
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
set service lldp interface eth2 disable

@ -1,8 +0,0 @@
---
- name: Remove Config
ansible.netcommon.cli_config:
config: "{{ lines }}"
vars:
lines: |
delete service lldp interface
delete service lldp

@ -1,46 +0,0 @@
---
- debug:
msg: "Start vyos_lldp_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- block:
- name: Delete attributes of given LLDP interfaces.
vyos.vyos.vyos_lldp_interfaces: &deleted
config:
- name: 'eth1'
- name: 'eth2'
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "populate | symmetric_difference(result['before']) |length == 0"
- name: Assert that the correct set of commands were generated
assert:
that:
- "deleted['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "deleted['after'] | symmetric_difference(result['after']) |length == 0"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "deleted['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,36 +0,0 @@
---
- debug:
msg: "START vyos_lldp_interfaces empty_config integration tests on connection={{ ansible_connection }}"
- name: Merged with empty config should give appropriate error message
vyos.vyos.vyos_lldp_interfaces:
config:
state: merged
register: result
ignore_errors: true
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state merged'
- name: Replaced with empty config should give appropriate error message
vyos.vyos.vyos_lldp_interfaces:
config:
state: replaced
register: result
ignore_errors: true
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state replaced'
- name: Overridden with empty config should give appropriate error message
vyos.vyos.vyos_lldp_interfaces:
config:
state: overridden
register: result
ignore_errors: true
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state overridden'

@ -1,58 +0,0 @@
---
- debug:
msg: "START vyos_lldp_interfaces merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
vyos.vyos.vyos_lldp_interfaces: &merged
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
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "merged['before'] | symmetric_difference(result['before']) |length == 0"
- name: Assert that correct set of commands were generated
assert:
that:
- "merged['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that after dicts was correctly generated
assert:
that:
- "merged['after'] | symmetric_difference(result['after']) |length == 0"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dicts were correctly generated
assert:
that:
- "merged['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,49 +0,0 @@
---
- debug:
msg: "START vyos_lldp_interfaces overridden integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_intf.yaml
- block:
- name: Overrides all device configuration with provided configuration
vyos.vyos.vyos_lldp_interfaces: &overridden
config:
- name: 'eth2'
location:
elin: '0000000911'
state: overridden
register: result
- name: Assert that before dicts were correctly generated
assert:
that:
- "populate_intf | symmetric_difference(result['before']) |length == 0"
- name: Assert that correct commands were generated
assert:
that:
- "overridden['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that after dicts were correctly generated
assert:
that:
- "overridden['after'] | symmetric_difference(result['after']) |length == 0"
- name: Overrides all device configuration with provided configurations (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *overridden
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dicts were correctly generated
assert:
that:
- "overridden['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,63 +0,0 @@
---
- debug:
msg: "START vyos_lldp_interfaces replaced integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Replace device configurations of listed LLDP interfaces with provided configurations
vyos.vyos.vyos_lldp_interfaces: &replaced
config:
- name: 'eth2'
enable: false
location:
civic_based:
country_code: 'US'
ca_info:
- ca_type: 0
ca_value: 'ENGLISH'
- name: 'eth1'
enable: false
location:
coordinate_based:
altitude: 2200
datum: 'WGS84'
longitude: '222.267255W'
latitude: '33.524449N'
state: replaced
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "replaced['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that before dicts are correctly generated
assert:
that:
- "populate | symmetric_difference(result['before']) |length == 0"
- name: Assert that after dict is correctly generated
assert:
that:
- "replaced['after'] | symmetric_difference(result['after']) |length == 0"
- name: Replace device configurations of listed LLDP interfaces with provided configurarions (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *replaced
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dict is correctly generated
assert:
that:
- "replaced['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,57 +0,0 @@
---
- debug:
msg: "START vyos_lldp_interfaces round trip integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Apply the provided configuration (base config)
vyos.vyos.vyos_lldp_interfaces:
config:
- name: 'eth1'
location:
civic_based:
country_code: 'US'
ca_info:
- ca_type: 0
ca_value: 'ENGLISH'
state: merged
register: base_config
- name: Gather lldp_interfaces facts
vyos.vyos.vyos_facts:
gather_subset:
- default
gather_network_resources:
- lldp_interfaces
- name: Apply the provided configuration (config to be reverted)
vyos.vyos.vyos_lldp_interfaces:
config:
- name: 'eth2'
location:
coordinate_based:
altitude: 2200
datum: 'WGS84'
longitude: '222.267255W'
latitude: '33.524449N'
state: merged
register: result
- name: Assert that changes were applied
assert:
that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Revert back to base config using facts round trip
vyos.vyos.vyos_lldp_interfaces:
config: "{{ ansible_facts['network_resources']['lldp_interfaces'] }}"
state: overridden
register: revert
- name: Assert that config was reverted
assert:
that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,130 +0,0 @@
---
merged:
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:
- 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'
populate:
- 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'
replaced:
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:
- name: 'eth2'
enable: false
location:
civic_based:
country_code: 'US'
ca_info:
- ca_type: 0
ca_value: 'ENGLISH'
- name: 'eth1'
enable: false
location:
coordinate_based:
altitude: 2200
datum: 'WGS84'
longitude: '222.267255W'
latitude: '33.524449N'
populate_intf:
- name: 'eth2'
enable: false
location:
civic_based:
country_code: 'US'
ca_info:
- ca_type: 0
ca_value: 'ENGLISH'
overridden:
commands:
- "delete service lldp interface eth2 location"
- "delete service lldp interface eth2 'disable'"
- "set service lldp interface eth2 location elin '0000000911'"
after:
- name: 'eth2'
location:
elin: 0000000911
deleted:
commands:
- "delete service lldp interface eth1"
- "delete service lldp interface eth2"
after: []
round_trip:
after:
- 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'

@ -1,13 +0,0 @@
---
- name: Ensure required interfaces are present in running-config
ansible.netcommon.cli_config:
config: "{{ lines }}"
vars:
lines: |
set interfaces ethernet eth0 address dhcp
set interfaces ethernet eth0 speed auto
set interfaces ethernet eth0 duplex auto
set interfaces ethernet eth1
set interfaces ethernet eth2
delete interfaces loopback lo
ignore_errors: true

@ -1,3 +0,0 @@
# Keeping incidental for efficiency, to avoid spinning up another VM
shippable/vyos/incidental
network/vyos

@ -1,14 +0,0 @@
- hosts: vyos
gather_facts: false
tasks:
- name: Run whoami
vyos.vyos.vyos_command:
commands:
- whoami
register: whoami
- assert:
that:
- whoami is successful
- whoami.stdout_lines[0][0] == 'atester'

@ -1,27 +0,0 @@
#!/usr/bin/env bash
set -eux
export ANSIBLE_ROLES_PATH=../
function cleanup {
ansible-playbook teardown.yml -i "$INVENTORY_PATH" "$@"
}
trap cleanup EXIT
ansible-playbook setup.yml -i "$INVENTORY_PATH" "$@"
# We need a nonempty file to override key with (empty file gives a
# lovely "list index out of range" error)
foo=$(mktemp)
echo hello > "$foo"
# We want to ensure that passwords make it to the network connection plugins
# because they follow a different path than the rest of the codebase.
# In setup.yml, we create a passworded user, and now we connect as that user
# to make sure the password we pass here successfully makes it to the plugin.
ansible-playbook \
-i "$INVENTORY_PATH" \
-e ansible_user=atester \
-e ansible_password=testymctest \
-e ansible_ssh_private_key_file="$foo" \
passworded_user.yml

@ -1,14 +0,0 @@
- hosts: vyos
connection: ansible.netcommon.network_cli
become: true
gather_facts: false
tasks:
- name: Create user with password
register: result
vyos.vyos.vyos_config:
lines:
- set system login user atester full-name "Ansible Tester"
- set system login user atester authentication plaintext-password testymctest
- set system login user jsmith level admin
- delete service ssh disable-password-authentication

@ -1,14 +0,0 @@
- hosts: vyos
connection: ansible.netcommon.network_cli
become: true
gather_facts: false
tasks:
- name: Get rid of user (undo everything from setup.yml)
register: result
vyos.vyos.vyos_config:
lines:
- delete system login user atester full-name "Ansible Tester"
- delete system login user atester authentication plaintext-password testymctest
- delete system login user jsmith level admin
- set service ssh disable-password-authentication

@ -1,2 +1 @@
ios/csr1000v collection=cisco.ios connection=ansible.netcommon.network_cli provider=aws arch=x86_64
vyos/1.1.8 collection=vyos.vyos connection=ansible.netcommon.network_cli provider=aws arch=x86_64

@ -137,7 +137,6 @@ test/integration/targets/win_script/files/test_script_with_args.ps1 pslint:PSAvo
test/integration/targets/win_script/files/test_script_with_splatting.ps1 pslint:PSAvoidUsingWriteHost # Keep
test/lib/ansible_test/_data/requirements/sanity.pslint.ps1 pslint:PSCustomUseLiteralPath # Uses wildcards on purpose
test/support/network-integration/collections/ansible_collections/cisco/ios/plugins/cliconf/ios.py pylint:arguments-renamed
test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py pylint:arguments-renamed
test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/module_utils/WebRequest.psm1 pslint!skip
test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/modules/win_uri.ps1 pslint!skip
test/support/windows-integration/plugins/modules/async_status.ps1 pslint!skip

@ -1,128 +0,0 @@
#
# (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 <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
import sys
import copy
from ansible_collections.ansible.netcommon.plugins.action.network import (
ActionModule as ActionNetworkModule,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
load_provider,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_provider_spec,
)
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 == "vyos_config" else False
persistent_connection = self._play_context.connection.split(".")[-1]
warnings = []
if persistent_connection == "network_cli":
provider = self._task.args.get("provider", {})
if any(provider.values()):
display.warning(
"provider is unnecessary when using network_cli and will be ignored"
)
del self._task.args["provider"]
elif self._play_context.connection == "local":
provider = load_provider(vyos_provider_spec, self._task.args)
pc = copy.deepcopy(self._play_context)
pc.connection = "ansible.netcommon.network_cli"
pc.network_os = "vyos.vyos.vyos"
pc.remote_addr = provider["host"] or self._play_context.remote_addr
pc.port = int(provider["port"] or self._play_context.port or 22)
pc.remote_user = (
provider["username"] or self._play_context.connection_user
)
pc.password = provider["password"] or self._play_context.password
pc.private_key_file = (
provider["ssh_keyfile"] or self._play_context.private_key_file
)
connection = self._shared_loader_obj.connection_loader.get(
"ansible.netcommon.persistent",
pc,
sys.stdin,
task_uuid=self._task._uuid,
)
# TODO: Remove below code after ansible minimal is cut out
if connection is None:
pc.connection = "network_cli"
pc.network_os = "vyos"
connection = self._shared_loader_obj.connection_loader.get(
"persistent", pc, sys.stdin, task_uuid=self._task._uuid
)
display.vvv(
"using connection plugin %s (was local)" % pc.connection,
pc.remote_addr,
)
command_timeout = (
int(provider["timeout"])
if provider["timeout"]
else connection.get_option("persistent_command_timeout")
)
connection.set_options(
direct={"persistent_command_timeout": command_timeout}
)
socket_path = connection.run()
display.vvvv("socket_path: %s" % socket_path, pc.remote_addr)
if not socket_path:
return {
"failed": True,
"msg": "unable to open shell. Please see: "
+ "https://docs.ansible.com/ansible/latest/network/user_guide/network_debug_troubleshooting.html#category-unable-to-open-shell",
}
task_vars["ansible_socket"] = socket_path
warnings.append(
[
"connection local support for this module is deprecated and will be removed in version 2.14, use connection %s"
% pc.connection
]
)
else:
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

@ -1,342 +0,0 @@
#
# (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 <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
DOCUMENTATION = """
---
author: Ansible Networking Team
cliconf: 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: "2.4"
"""
import re
import json
from collections.abc import Mapping
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils.common.text.converters import to_text
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.plugins.cliconf import CliconfBase
class Cliconf(CliconfBase):
def get_device_info(self):
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 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()
return 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
):
resp = {}
operations = self.get_device_operations()
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"
)
.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,
output=None,
newline=True,
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
)
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"
)
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"
)
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
]
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()
]
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`"
)
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
)
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["rpc"] += [
"commit",
"discard_changes",
"get_diff",
"run_commands",
]
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"
)

@ -1,64 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import annotations
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r"""options:
provider:
description:
- B(Deprecated)
- 'Starting with Ansible 2.5 we recommend using C(connection: network_cli).'
- For more information please see the L(Network Guide, ../network/getting_started/network_differences.html#multiple-communication-protocols).
- HORIZONTALLINE
- A dict object containing connection details.
type: dict
suboptions:
host:
description:
- Specifies the DNS host name or address for connecting to the remote device
over the specified transport. The value of host is used as the destination
address for the transport.
type: str
required: true
port:
description:
- Specifies the port to use when building the connection to the remote device.
type: int
default: 22
username:
description:
- Configures the username to use to authenticate the connection to the remote
device. This value is used to authenticate the SSH session. If the value
is not specified in the task, the value of environment variable C(ANSIBLE_NET_USERNAME)
will be used instead.
type: str
password:
description:
- Specifies the password to use to authenticate the connection to the remote
device. This value is used to authenticate the SSH session. If the value
is not specified in the task, the value of environment variable C(ANSIBLE_NET_PASSWORD)
will be used instead.
type: str
timeout:
description:
- Specifies the timeout in seconds for communicating with the network device
for either connecting or sending commands. If the timeout is exceeded before
the operation is completed, the module will error.
type: int
default: 10
ssh_keyfile:
description:
- Specifies the SSH key to use to authenticate the connection to the remote
device. This value is the path to the key used to authenticate the SSH
session. If the value is not specified in the task, the value of environment
variable C(ANSIBLE_NET_SSH_KEYFILE) will be used instead.
type: path
notes:
- For more information on using Ansible to manage network devices see the :ref:`Ansible
Network Guide <network_guide>`
"""

@ -1,20 +0,0 @@
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The arg spec for the vyos facts module.
"""
from __future__ import annotations
class FactsArgs(object): # pylint: disable=R0903
""" The arg spec for the vyos facts module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"gather_subset": dict(default=["!config"], type="list"),
"gather_network_resources": dict(type="list"),
}

@ -1,261 +0,0 @@
#
# -*- 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 annotations
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"
},
"port_group": {"type": "str"},
},
"type": "dict",
},
"port": {"type": "str"},
},
"type": "dict",
},
"disabled": {"type": "bool"},
"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",
},
"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"
},
"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

@ -1,67 +0,0 @@
# 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_interfaces module
"""
from __future__ import annotations
class InterfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"description": {"type": "str"},
"duplex": {"choices": ["full", "half", "auto"]},
"enabled": {"default": True, "type": "bool"},
"mtu": {"type": "int"},
"name": {"required": True, "type": "str"},
"speed": {
"choices": ["auto", "10", "100", "1000", "2500", "10000"],
"type": "str",
},
"vifs": {
"elements": "dict",
"options": {
"vlan_id": {"type": "int"},
"description": {"type": "str"},
"enabled": {"default": True, "type": "bool"},
"mtu": {"type": "int"},
},
"type": "list",
},
},
"type": "list",
},
"state": {
"choices": ["merged", "replaced", "overridden", "deleted"],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301

@ -1,79 +0,0 @@
#
# -*- 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_l3_interfaces module
"""
from __future__ import annotations
class L3_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_l3_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"ipv4": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"ipv6": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"name": {"required": True, "type": "str"},
"vifs": {
"elements": "dict",
"options": {
"ipv4": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"ipv6": {
"elements": "dict",
"options": {"address": {"type": "str"}},
"type": "list",
},
"vlan_id": {"type": "int"},
},
"type": "list",
},
},
"type": "list",
},
"state": {
"choices": ["merged", "replaced", "overridden", "deleted"],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301

@ -1,78 +0,0 @@
# 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_lag_interfaces module
"""
from __future__ import annotations
class Lag_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_lag_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"arp_monitor": {
"options": {
"interval": {"type": "int"},
"target": {"type": "list"},
},
"type": "dict",
},
"hash_policy": {
"choices": ["layer2", "layer2+3", "layer3+4"],
"type": "str",
},
"members": {
"elements": "dict",
"options": {"member": {"type": "str"}},
"type": "list",
},
"mode": {
"choices": [
"802.3ad",
"active-backup",
"broadcast",
"round-robin",
"transmit-load-balance",
"adaptive-load-balance",
"xor-hash",
],
"type": "str",
},
"name": {"required": True, "type": "str"},
"primary": {"type": "str"},
},
"type": "list",
},
"state": {
"choices": ["merged", "replaced", "overridden", "deleted"],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301

@ -1,54 +0,0 @@
# 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_lldp_global module
"""
from __future__ import annotations
class Lldp_globalArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_lldp_global module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"options": {
"address": {"type": "str"},
"enable": {"type": "bool"},
"legacy_protocols": {
"choices": ["cdp", "edp", "fdp", "sonmp"],
"type": "list",
},
"snmp": {"type": "str"},
},
"type": "dict",
},
"state": {
"choices": ["merged", "replaced", "deleted"],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301

@ -1,87 +0,0 @@
#
# -*- 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_lldp_interfaces module
"""
from __future__ import annotations
class Lldp_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_lldp_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"enable": {"default": True, "type": "bool"},
"location": {
"options": {
"civic_based": {
"options": {
"ca_info": {
"elements": "dict",
"options": {
"ca_type": {"type": "int"},
"ca_value": {"type": "str"},
},
"type": "list",
},
"country_code": {
"required": True,
"type": "str",
},
},
"type": "dict",
},
"coordinate_based": {
"options": {
"altitude": {"type": "int"},
"datum": {
"choices": ["WGS84", "NAD83", "MLLW"],
"type": "str",
},
"latitude": {"required": True, "type": "str"},
"longitude": {"required": True, "type": "str"},
},
"type": "dict",
},
"elin": {"type": "str"},
},
"type": "dict",
},
"name": {"required": True, "type": "str"},
},
"type": "list",
},
"state": {
"choices": ["merged", "replaced", "overridden", "deleted"],
"default": "merged",
"type": "str",
},
} # pylint: disable=C0301

@ -1,97 +0,0 @@
#
# -*- 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_static_routes module
"""
from __future__ import annotations
class Static_routesArgs(object): # pylint: disable=R0903
"""The arg spec for the vyos_static_routes module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
"config": {
"elements": "dict",
"options": {
"address_families": {
"elements": "dict",
"options": {
"afi": {
"choices": ["ipv4", "ipv6"],
"required": True,
"type": "str",
},
"routes": {
"elements": "dict",
"options": {
"blackhole_config": {
"options": {
"distance": {"type": "int"},
"type": {"type": "str"},
},
"type": "dict",
},
"dest": {"required": True, "type": "str"},
"next_hops": {
"elements": "dict",
"options": {
"admin_distance": {"type": "int"},
"enabled": {"type": "bool"},
"forward_router_address": {
"required": True,
"type": "str",
},
"interface": {"type": "str"},
},
"type": "list",
},
},
"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

@ -1,436 +0,0 @@
#
# -*- 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 annotations
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,
)
from ansible.module_utils.six import iteritems
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,
)
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):
""" 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
)
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}
commands = list()
warnings = list()
existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
commands.extend(self.set_config(existing_lldp_interfaces_facts))
if commands:
if self._module.check_mode:
resp = self._connection.edit_config(commands, commit=False)
else:
resp = self._connection.edit_config(commands)
result["changed"] = True
result["commands"] = commands
if self._module._diff:
result["diff"] = resp["diff"] if result["changed"] else None
changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
result["before"] = existing_lldp_interfaces_facts
if result["changed"]:
result["after"] = 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 = []
state = self._module.params["state"]
if state in ("merged", "replaced", "overridden") and not want:
self._module.fail_json(
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 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)
)
else:
for have_item in have:
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 state == "merged":
commands.extend(
self._state_merged(want=want_item, have=have_item)
)
else:
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)
)
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)
)
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
commands.extend(self._add_location(lldp_name, want, have))
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")
)
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)
)
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)
)
)
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"
):
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
)
)
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)
)
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)
)
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"
)
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"
)
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

@ -1,82 +0,0 @@
# 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 annotations
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.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.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.legacy.base import (
Default,
Neighbors,
Config,
)
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,
)
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
):
""" 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
)
if self.VALID_LEGACY_GATHER_SUBSETS:
self.get_network_legacy_facts(
FACT_LEGACY_SUBSETS, legacy_facts_type
)
return self.ansible_facts, self._warnings

@ -1,379 +0,0 @@
#
# -*- 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 annotations
from re import findall, search, M
from copy import deepcopy
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
)
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}
)
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):
rule_regex = r" %s .+$" % 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",
"action",
"protocol",
"fragment",
"disabled",
"description",
]
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"]
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 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
"""
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

@ -1,132 +0,0 @@
#
# -*- 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 annotations
from re import findall, M
from copy import deepcopy
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 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 = connection.get_config(flags=["| grep interfaces"])
objs = []
interface_names = findall(
r"^set interfaces (?:ethernet|bonding|vti|loopback|vxlan) (?:\'*)(\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}
)
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["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)

@ -1,141 +0,0 @@
#
# -*- 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 annotations
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.ansible.netcommon.plugins.module_utils.compat import (
ipaddress,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import (
L3_interfacesArgs,
)
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}
)
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 = ipaddress.ip_address(item.split("/")[0]).version
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)

@ -1,151 +0,0 @@
#
# -*- 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 annotations
from re import findall, search, M
from copy import deepcopy
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)
output = connection.run_commands(
["show interfaces bonding " + lag + " slaves"]
)
lines = output[0].splitlines()
members = []
member = {}
if len(lines) > 1:
for line in lines[2:]:
splitted_line = line.split()
if len(splitted_line) > 1:
member["member"] = splitted_line[0]
members.append(member)
else:
members = []
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}
)
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)
)
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

@ -1,161 +0,0 @@
# -*- 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 annotations
import platform
import re
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
run_commands,
get_capabilities,
)
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 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
elif 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)

@ -1,115 +0,0 @@
#
# -*- 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 annotations
from re import findall, M
from copy import deepcopy
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)
)
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)

@ -1,153 +0,0 @@
#
# -*- 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 annotations
from re import findall, search, M
from copy import deepcopy
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)
)
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
)
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

@ -1,180 +0,0 @@
#
# -*- 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 annotations
from re import findall, search, M
from copy import deepcopy
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
)
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}
)
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("'")
}
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 "interface" in nh_info.keys():
element["interface"] = nh_info["interface"]
if "admin_distance" in nh_info.keys():
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

@ -1,230 +0,0 @@
# -*- 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 annotations
from ansible.module_utils.six import iteritems
from ansible_collections.ansible.netcommon.plugins.module_utils.compat import (
ipaddress,
)
def search_obj_in_list(name, lst, key="name"):
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"
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
]
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
]
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
]
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
),
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:
"""
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"

@ -1,126 +0,0 @@
# 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 annotations
import json
from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.connection import Connection, ConnectionError
_DEVICE_CONFIGS = {}
vyos_provider_spec = {
"host": dict(),
"port": dict(type="int"),
"username": dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])),
"password": dict(
fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True
),
"ssh_keyfile": dict(
fallback=(env_fallback, ["ANSIBLE_NET_SSH_KEYFILE"]), type="path"
),
"timeout": dict(type="int"),
}
vyos_argument_spec = {
"provider": dict(
type="dict", options=vyos_provider_spec, removed_in_version=2.14
),
}
def get_provider_argspec():
return vyos_provider_spec
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
)
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
)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
return response.get("diff")

@ -1,224 +0,0 @@
#!/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 <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "network",
}
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.
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.
required: true
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.
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
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
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
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(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
"""
EXAMPLES = """
tasks:
- name: show configuration on ethernet devices eth0 and eth1
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_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_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.common.text.converters 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,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
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),
wait_for=dict(type="list", aliases=["waitfor"]),
match=dict(default="all", choices=["all", "any"]),
retries=dict(default=10, type="int"),
interval=dict(default=1, type="int"),
)
spec.update(vyos_argument_spec)
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 dummy 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)), }
)
module.exit_json(**result)
if __name__ == "__main__":
main()

@ -1,355 +0,0 @@
#!/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 <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "network",
}
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.
extends_documentation_fragment:
- vyos.vyos.vyos
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
options:
lines:
description:
- The ordered set of configuration lines to be managed and compared with the existing
configuration on the remote device.
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.
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.
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
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.
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 <hostname>_config.<current-date>@<current-time>
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_config:
lines:
- set system host-name {{ inventory_hostname }}
- set service lldp
- delete service dhcp-server
- name: backup and load from file
vyos_config:
src: vyos.cfg
backup: yes
- name: render a Jinja2 template onto the VyOS router
vyos_config:
src: vyos_template.j2
- name: for idempotency, use full-form commands
vyos_config:
lines:
# - set int eth eth2 description 'OUTSIDE'
- set interface ethernet eth2 description 'OUTSIDE'
- name: configurable backup path
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.common.text.converters 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 (
vyos_argument_spec,
get_connection,
)
DEFAULT_COMMENT = "configured by vyos_config"
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 = format_commands(contents.splitlines())
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"
)
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"),
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),
)
argument_spec.update(vyos_argument_spec)
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]":
run_commands(module, commands=["save"])
result["changed"] = True
run_commands(module, commands=["exit"])
module.exit_json(**result)
if __name__ == "__main__":
main()

@ -1,175 +0,0 @@
#!/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)
"""
The module file for vyos_facts
"""
from __future__ import annotations
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": [u"preview"],
"supported_by": "network",
}
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.
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(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 all, default, config, and neighbors. Can specify
a list of values to include a larger subset. Values can also be used with an
initial C(M(!)) to specify that a specific subset should not be collected.
required: false
default: '!config'
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(M(!)) 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'.
required: false
"""
EXAMPLES = """
# Gather all facts
- vyos_facts:
gather_subset: all
gather_network_resources: all
# collect only the config and default facts
- vyos_facts:
gather_subset: config
# collect everything exception the config
- vyos_facts:
gather_subset: "!config"
# Collect only the interfaces facts
- vyos_facts:
gather_subset:
- '!all'
- '!min'
gather_network_resources:
- interfaces
# Do not collect interfaces facts
- vyos_facts:
gather_network_resources:
- "!interfaces"
# Collect interfaces and minimal default facts
- 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,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def main():
"""
Main entry point for module execution
:returns: ansible_facts
"""
argument_spec = FactsArgs.argument_spec
argument_spec.update(vyos_argument_spec)
module = AnsibleModule(
argument_spec=argument_spec, supports_check_mode=True
)
warnings = []
if module.params["gather_subset"] == "!config":
warnings.append(
"default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards"
)
result = Facts(module).get_facts()
ansible_facts, additional_warnings = result
warnings.extend(additional_warnings)
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
if __name__ == "__main__":
main()

@ -1,512 +0,0 @@
#!/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 annotations
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "network",
}
DOCUMENTATION = """module: vyos_lldp_interfaces
short_description: Manages attributes of lldp interfaces on VyOS devices.
description: This module manages attributes of lldp interfaces on VyOS network devices.
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(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
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
suboptions:
ca_type:
description: LLDP-MED Civic Address type.
type: int
required: true
ca_value:
description: LLDP-MED Civic Address value.
type: str
required: true
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
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vyos@vyos:~$ show configuration commands | grep lldp
#
- name: Merge provided configuration with device configuration
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_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_lag_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_lag_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'
"""
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", "overridden", ("config",)),
]
module = AnsibleModule(
argument_spec=Lldp_interfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
)
result = Lldp_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()

@ -1,52 +0,0 @@
#
# (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 <http://www.gnu.org/licenses/>.
#
from __future__ import annotations
import os
import re
from ansible.plugins.terminal import TerminalBase
from ansible.errors import AnsibleConnectionFailure
class TerminalModule(TerminalBase):
terminal_stdout_re = [
re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
re.compile(br"\@[\w\-\.]+:\S+?[>#\$] ?$"),
]
terminal_stderr_re = [
re.compile(br"\n\s*Invalid command:"),
re.compile(br"\nCommit failed"),
re.compile(br"\n\s+Set failed"),
]
terminal_length = os.getenv("ANSIBLE_VYOS_TERMINAL_LENGTH", 10000)
def on_open_shell(self):
try:
for cmd in (b"set terminal length 0", b"set terminal width 512"):
self._exec_cli_command(cmd)
self._exec_cli_command(
b"set terminal length %d" % self.terminal_length
)
except AnsibleConnectionFailure:
raise AnsibleConnectionFailure("unable to set terminal parameters")
Loading…
Cancel
Save