mirror of https://github.com/ansible/ansible.git
The VyOS remote image required for running the tests is no longer functional.
(cherry picked from commit 79f819dc54
)
pull/83677/head
parent
f02fd32fe0
commit
03446c7f23
@ -1 +0,0 @@
|
||||
network.sh
|
@ -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,3 +0,0 @@
|
||||
---
|
||||
testcase: "*"
|
||||
test_items: []
|
@ -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,3 +0,0 @@
|
||||
set service lldp
|
||||
set protocols static
|
||||
|
@ -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 @@
|
||||
---
|
||||
testcase: "[^_].*"
|
||||
test_items: []
|
@ -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 +0,0 @@
|
||||
hidden
|
@ -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,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…
Reference in New Issue