diff --git a/changelogs/fragments/T6890_all_boolean.yml b/changelogs/fragments/T6890_all_boolean.yml new file mode 100644 index 00000000..2adec1d9 --- /dev/null +++ b/changelogs/fragments/T6890_all_boolean.yml @@ -0,0 +1,2 @@ +trivial: + - Change string 'yes/now' to recommended 'true/false' in the collection's documentation, where practical. diff --git a/docs/vyos.vyos.vyos_config_module.rst b/docs/vyos.vyos.vyos_config_module.rst index b5b9f8b9..bf91bb0f 100644 --- a/docs/vyos.vyos.vyos_config_module.rst +++ b/docs/vyos.vyos.vyos_config_module.rst @@ -1,458 +1,458 @@ .. _vyos.vyos.vyos_config_module: ********************* vyos.vyos.vyos_config ********************* **Manage VyOS configuration on remote device** Version added: 1.0.0 .. contents:: :local: :depth: 1 Synopsis -------- - 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. Parameters ---------- .. raw:: html
Parameter Choices/Defaults Comments
allow_password_change
string
    Choices:
  • all
  • plaintext ←
  • encrypted
  • none
The allow_password_change argument specifies whether any configuration lines which would change a user's password should be filtered out. By default only plaintext password changes are allowed and any encrypted-password keys are filtered out. In order to allow all password updates, both plaintext and encrypted, set this argument to all.
backup
boolean
    Choices:
  • no ←
  • yes
The backup argument will backup the current devices active configuration to the Ansible control host prior to making any changes. If the 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.
backup_options
dictionary
-
This is a dict object containing configurable options related to backup file path. The value of this option is read only when backup is set to yes, if backup is set to no this option will be silently ignored.
+
This is a dict object containing configurable options related to backup file path. The value of this option is read only when backup is set to true, if backup is set to false this option will be silently ignored.
dir_path
path
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 filename or default filename as described in filename options description. If the path value is not given in that case a backup directory will be created in the current working directory and backup configuration will be copied in filename within backup directory.
filename
string
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>
comment
string
Default:
"configured by vyos_config"
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.
config
string
The config argument specifies the base configuration to use to compare against the desired configuration. If this value is not specified, the module will automatically retrieve the current active configuration from the remote device. The configuration lines in the option value should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff.
confirm
string
    Choices:
  • automatic
  • manual
  • none ←
The confirm argument will tell vyos to revert to the previous configuration if not explicitly confirmed after applying the new config. When set to automatic this module will automatically confirm the configuration, if the current session remains working with the new config. When set to manual, this module does not issue the confirmation itself.
confirm_timeout
integer
Default:
10
Minutes to wait for confirmation before reverting the configuration. Does not apply when confirm is set to none .
lines
list / elements=string
The ordered set of commands that should be configured in the section. The commands must be the exact same commands as found in the device running-config as found in the device running-config to ensure idempotency and correct diff. Be sure to note the configuration command syntax as some commands are automatically modified by the device config parser.
match
string
    Choices:
  • line ←
  • none
The 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 match argument is set to none the active configuration is ignored and the configuration is always loaded.
save
boolean
    Choices:
  • no ←
  • yes
-
The 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.
+
The 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.
src
path
The src argument specifies the path to the source config file to load. The source config file can either be in bracket format or set format. The source file can include Jinja2 template variables. The configuration lines in the source file should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff.

Notes ----- .. note:: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection ``ansible.netcommon.network_cli``. See `the VyOS OS Platform Options <../network/user_guide/platform_vyos.html>`_. - To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they appear if present in the running configuration on device including the indentation. - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` Examples -------- .. code-block:: yaml - name: configure the remote device vyos.vyos.vyos_config: lines: - set system host-name {{ inventory_hostname }} - set service lldp - delete service dhcp-server - name: backup and load from file vyos.vyos.vyos_config: src: vyos.cfg backup: true - name: render a Jinja2 template onto the VyOS router vyos.vyos.vyos_config: src: vyos_template.j2 - name: revert after ten minutes, if connection is lost vyos.vyos.vyos_config: src: vyos_template.j2 confirm: automatic - name: for idempotency, use full-form commands vyos.vyos.vyos_config: lines: # - set int eth eth2 description 'OUTSIDE' - set interface ethernet eth2 description 'OUTSIDE' - name: configurable backup path vyos.vyos.vyos_config: backup: true backup_options: filename: backup.cfg dir_path: /home/user Return Values ------------- Common return values are documented `here `_, the following are the fields unique to this module: .. raw:: html
Key Returned Description
backup_path
string
when backup is yes
The full path to the backup file

Sample:
/playbooks/ansible/backup/vyos_config.2016-07-16@22:28:34
commands
list
always
The list of configuration commands sent to the device

Sample:
['...', '...']
date
string
when backup is yes
The date extracted from the backup file name

Sample:
2016-07-16
filename
string
when backup is yes and filename is not specified in backup options
The name of the backup file

Sample:
vyos_config.2016-07-16@22:28:34
filtered
list
always
The list of configuration commands removed to avoid a load failure

Sample:
['...', '...']
shortname
string
when backup is yes and filename is not specified in backup options
The full path to the backup file excluding the timestamp

Sample:
/playbooks/ansible/backup/vyos_config
time
string
when backup is yes
The time extracted from the backup file name

Sample:
22:28:34


Status ------ Authors ~~~~~~~ - Nathaniel Case (@Qalthos) diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py index 2407ce01..666bb629 100644 --- a/plugins/modules/vyos_config.py +++ b/plugins/modules/vyos_config.py @@ -1,446 +1,451 @@ #!/usr/bin/python # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_config author: Nathaniel Case (@Qalthos) short_description: Manage VyOS configuration on remote device description: - This module provides configuration file management of VyOS devices. It provides arguments for managing both the configuration file and state of the active configuration. All configuration statements are based on `set` and `delete` commands in the device configuration. version_added: 1.0.0 extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). - To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they appear if present in the running configuration on device including the indentation. options: lines: description: - The ordered set of commands that should be configured in the section. The commands must be the exact same commands as found in the device running-config as found in the device running-config to ensure idempotency and correct diff. Be sure to note the configuration command syntax as some commands are automatically modified by the device config parser. type: list elements: str src: description: - The C(src) argument specifies the path to the source config file to load. The source config file can either be in bracket format or set format. The source file can include Jinja2 template variables. The configuration lines in the source file should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff. type: path match: description: - The C(match) argument controls the method used to match against the current active configuration. By default, the desired config is matched against the active config and the deltas are loaded. If the C(match) argument is set to C(none) the active configuration is ignored and the configuration is always loaded. type: str default: line choices: - line - none backup: description: - The C(backup) argument will backup the current devices active configuration to the Ansible control host prior to making any changes. If the C(backup_options) value is not given, the backup file will be located in the backup folder in the playbook root directory or role root directory, if playbook is part of an ansible role. If the directory does not exist, it is created. type: bool - default: no + default: false comment: description: - Allows a commit description to be specified to be included when the configuration is committed. If the configuration is not changed or committed, this argument is ignored. default: configured by vyos_config type: str confirm: description: - The C(confirm) argument will tell vyos to revert to the previous configuration if not explicitly confirmed after applying the new config. When set to C(automatic) this module will automatically confirm the configuration, if the current session remains working with the new config. When set to C(manual), this module does not issue the confirmation itself. type: str default: none choices: - automatic - manual - none confirm_timeout: description: - Minutes to wait for confirmation before reverting the configuration. Does not apply when C(confirm) is set to C(none) . type: int default: 10 config: description: - The C(config) argument specifies the base configuration to use to compare against the desired configuration. If this value is not specified, the module will automatically retrieve the current active configuration from the remote device. The configuration lines in the option value should be similar to how it will appear if present in the running-configuration of the device including indentation to ensure idempotency and correct diff. type: str save: description: - The C(save) argument controls whether or not changes made to the active configuration are saved to disk. This is independent of committing the config. When set - to True, the active configuration is saved. + to C(true), the active configuration is saved. type: bool - default: no + default: false 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. + path. The value of this option is read only when C(backup) is set to C(true), + if C(backup) is set to C(false) this option will be silently ignored. suboptions: filename: description: - The filename to be used to store the backup configuration. If the filename is not given it will be generated based on the hostname, current time and date in format defined by _config.@ type: str dir_path: description: - This option provides the path ending with directory name in which the backup configuration file will be stored. If the directory does not exist it will be first created and the filename is either the value of C(filename) or default filename as described in C(filename) options description. If the path value is not given in that case a I(backup) directory will be created in the current working directory and backup configuration will be copied in C(filename) within I(backup) directory. type: path type: dict allow_password_change: description: - The C(allow_password_change) argument specifies whether any configuration lines which would change a user's password should be filtered out. By default only plaintext password changes are allowed and any encrypted-password keys are filtered out. In order to allow all password updates, both plaintext and encrypted, set this argument to C(all). type: str default: plaintext choices: - all - plaintext - encrypted - none """ EXAMPLES = """ - name: configure the remote device vyos.vyos.vyos_config: lines: - set system host-name {{ inventory_hostname }} - set service lldp - delete service dhcp-server - name: backup and load from file vyos.vyos.vyos_config: src: vyos.cfg backup: true - name: render a Jinja2 template onto the VyOS router vyos.vyos.vyos_config: src: vyos_template.j2 - name: revert after ten minutes, if connection is lost vyos.vyos.vyos_config: src: vyos_template.j2 confirm: automatic - name: for idempotency, use full-form commands vyos.vyos.vyos_config: lines: # - set int eth eth2 description 'OUTSIDE' - set interface ethernet eth2 description 'OUTSIDE' - name: configurable backup path vyos.vyos.vyos_config: backup: true backup_options: filename: backup.cfg dir_path: /home/user """ RETURN = """ commands: description: The list of configuration commands sent to the device returned: always type: list sample: ['...', '...'] filtered: description: The list of configuration commands removed to avoid a load failure returned: always type: list sample: ['...', '...'] backup_path: description: The full path to the backup file returned: when backup is yes type: str sample: /playbooks/ansible/backup/vyos_config.2016-07-16@22:28:34 filename: description: The name of the backup file returned: when backup is yes and filename is not specified in backup options type: str sample: vyos_config.2016-07-16@22:28:34 shortname: description: The full path to the backup file excluding the timestamp returned: when backup is yes and filename is not specified in backup options type: str sample: /playbooks/ansible/backup/vyos_config date: description: The date extracted from the backup file name returned: when backup is yes type: str sample: "2016-07-16" time: description: The time extracted from the backup file name returned: when backup is yes type: str sample: "22:28:34" """ import re from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.connection import ConnectionError from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_config, get_connection, load_config, run_commands, ) DEFAULT_COMMENT = "configured by vyos_config" -PASSWORD_NEEDLE = re.compile(r"set system login user \S+ authentication (encrypted|plaintext)-password") +PASSWORD_NEEDLE = re.compile( + r"set system login user \S+ authentication (encrypted|plaintext)-password", +) def get_candidate(module): contents = module.params["src"] or module.params["lines"] if module.params["src"]: contents = contents.splitlines() if len(contents) > 0: line = contents[0].split() if len(line) > 0 and line[0] in ("set", "delete"): contents = format_commands(contents) contents = "\n".join(contents) return contents def format_commands(commands): """ This function format the input commands and removes the prepend white spaces for command lines having 'set' or 'delete' and it skips empty lines. :param commands: :return: list of commands """ return [ line.strip() if line.split()[0] in ("set", "delete") else line for line in commands if len(line.strip()) > 0 ] def diff_config(commands, config): config = [str(c).replace("'", "") for c in config.splitlines()] updates = list() visited = set() for line in commands: item = str(line).replace("'", "") if not item.startswith("set") and not item.startswith("delete"): raise ValueError("line must start with either `set` or `delete`") elif item.startswith("set") and item not in config: updates.append(line) elif item.startswith("delete"): if not config: updates.append(line) else: item = re.sub(r"delete", "set", item) for entry in config: if entry.startswith(item) and line not in visited: updates.append(line) visited.add(line) return list(updates) def sanitize_config(config, result, allow): result["filtered"] = list() if allow == "all": return index_to_filter = list() for index, line in enumerate(list(config)): found = PASSWORD_NEEDLE.search(line) if found is None: continue if allow == found[1]: continue 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") allow_password_change = module.params["allow_password_change"] sanitize_config(commands, result, allow=allow_password_change) result["commands"] = commands commit = not module.check_mode comment = module.params["comment"] confirm = None if module.params["confirm"] == "automatic" or module.params["confirm"] == "manual": confirm = module.params["confirm_timeout"] diff = None if commands: diff = load_config(module, commands, commit=commit, comment=comment, confirm=confirm) if module.params["confirm"] == "automatic": run_commands(module, ["configure", "confirm", "exit"]) 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", elements="str"), match=dict(default="line", choices=["line", "none"]), comment=dict(default=DEFAULT_COMMENT), - confirm=dict(choices=["automatic", "manual", "none"], default='none'), + confirm=dict(choices=["automatic", "manual", "none"], default="none"), confirm_timeout=dict(type="int", default=10), config=dict(), backup=dict(type="bool", default=False), backup_options=dict(type="dict", options=backup_spec), save=dict(type="bool", default=False), - allow_password_change=dict(default="plaintext", choices=["all", "encrypted", "plaintext", "none"]) + allow_password_change=dict( + default="plaintext", + choices=["all", "encrypted", "plaintext", "none"], + ), ) 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 not in { "[edit]", "No changes between working and saved configurations.\n\n[edit]", }: if not module.check_mode: run_commands(module, commands=["save"]) result["changed"] = True run_commands(module, commands=["exit"]) if result.get("changed") and any((module.params["src"], module.params["lines"])): msg = ( "To ensure idempotency and correct diff the input configuration lines should be" " similar to how they appear if present in" " the running configuration on device" ) if module.params["src"]: msg += " including the indentation" if "warnings" in result: result["warnings"].append(msg) else: result["warnings"] = msg module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_user.py b/plugins/modules/vyos_user.py index e47f5a05..0c79ce30 100644 --- a/plugins/modules/vyos_user.py +++ b/plugins/modules/vyos_user.py @@ -1,517 +1,517 @@ #!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function __metaclass__ = type # (c) 2017, Ansible by Red Hat, inc # # This file is part of Ansible by Red Hat # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # DOCUMENTATION = """ module: vyos_user author: Trishna Guha (@trishnaguha) short_description: Manage the collection of local users on VyOS device description: - This module provides declarative management of the local usernames configured on network devices. It allows playbooks to manage either individual usernames or the collection of usernames in the current running config. It also supports purging usernames from the configuration that are not explicitly defined. version_added: 1.0.0 extends_documentation_fragment: - vyos.vyos.vyos notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: aggregate: description: - The set of username objects to be configured on the remote VyOS device. The list entries can either be the username or a hash of username and properties. This argument is mutually exclusive with the C(name) argument. aliases: - users - collection type: list elements: dict suboptions: name: description: - The username to be configured on the VyOS device. This argument accepts a string value and is mutually exclusive with the C(aggregate) argument. - required: True + required: true type: str full_name: description: - The C(full_name) argument provides the full name of the user account to be created on the remote device. This argument accepts any text string value. type: str encrypted_password: description: - The encrypted password of the user account on the remote device. Note that unlike the C(configured_password) argument, this argument ignores the C(update_password) and updates if the value is different from the one in the device running config. type: str configured_password: description: - The password to be configured on the VyOS device. The password needs to be provided in clear and it will be encrypted on the device. type: str update_password: description: - Since passwords are encrypted in the device running config, this argument will instruct the module when to change the password. When set to C(always), the password will always be updated in the device and when set to C(on_create) the password will be updated only if the username is created. type: str choices: - on_create - always state: description: - Configures the state of the username definition as it relates to the device operational configuration. When set to I(present), the username(s) should be configured in the device active configuration and when set to I(absent) the username(s) should not be in the device active configuration type: str choices: - present - absent public_keys: &public_keys description: - Public keys for authentiction over SSH. type: list elements: dict suboptions: name: description: Name of the key (usually in the form of user@hostname) required: true type: str key: description: Public key string (base64 encoded) required: true type: str type: description: Type of the key required: true type: str choices: - ssh-dss - ssh-rsa - ecdsa-sha2-nistp256 - ecdsa-sha2-nistp384 - ssh-ed25519 - ecdsa-sha2-nistp521 name: description: - The username to be configured on the VyOS device. This argument accepts a string value and is mutually exclusive with the C(aggregate) argument. type: str full_name: description: - The C(full_name) argument provides the full name of the user account to be created on the remote device. This argument accepts any text string value. type: str encrypted_password: description: - The encrypted password of the user account on the remote device. Note that unlike the C(configured_password) argument, this argument ignores the C(update_password) and updates if the value is different from the one in the device running config. type: str configured_password: description: - The password to be configured on the VyOS device. The password needs to be provided in clear and it will be encrypted on the device. type: str update_password: description: - Since passwords are encrypted in the device running config, this argument will instruct the module when to change the password. When set to C(always), the password will always be updated in the device and when set to C(on_create) the password will be updated only if the username is created. default: always type: str choices: - on_create - always public_keys: *public_keys purge: description: - Instructs the module to consider the resource definition absolute. It will remove any previously configured usernames on the device with the exception of the `admin` user (the current defined set of users). type: bool default: false state: description: - Configures the state of the username definition as it relates to the device operational configuration. When set to I(present), the username(s) should be configured in the device active configuration and when set to I(absent) the username(s) should not be in the device active configuration type: str default: present choices: - present - absent """ EXAMPLES = """ - name: create a new user vyos.vyos.vyos_user: name: ansible configured_password: password state: present - name: remove all users except admin vyos.vyos.vyos_user: purge: true - name: set multiple users to level operator vyos.vyos.vyos_user: aggregate: - name: netop - name: netend state: present - name: Change Password for User netop vyos.vyos.vyos_user: name: netop configured_password: '{{ new_password }}' update_password: always state: present """ RETURN = """ commands: description: The list of configuration mode commands to send to the device returned: always type: list sample: - set system login user authentication plaintext-password password """ import re import shlex from copy import deepcopy from functools import partial from ansible.module_utils.basic import AnsibleModule from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_default_spec, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_config, load_config, ) def spec_to_commands(updates, module): commands = list() update_password = module.params["update_password"] def needs_update(want, have, x): return want.get(x) and (want.get(x) != have.get(x)) def add(command, want, x): command.append("set system login user %s %s" % (want["name"], x)) for update in updates: want, have = update if want["state"] == "absent": commands.append("delete system login user %s" % want["name"]) continue if needs_update(want, have, "full_name"): add(commands, want, "full-name '%s'" % want["full_name"]) # look both ways for public_keys to handle replacement want_keys = want.get("public_keys") or dict() have_keys = have.get("public_keys") or dict() for key_name in want_keys: key = want_keys[key_name] if key_name not in have_keys or key != have_keys[key_name]: add( commands, want, "authentication public-keys %s key '%s'" % (key["name"], key["key"]), ) add( commands, want, "authentication public-keys %s type '%s'" % (key["name"], key["type"]), ) for key_name in have_keys: if key_name not in want_keys: commands.append( "delete system login user %s authentication public-keys %s" % (want["name"], key_name), ) if needs_update(want, have, "encrypted_password"): add( commands, want, "authentication encrypted-password '%s'" % want["encrypted_password"], ) if needs_update(want, have, "configured_password"): if update_password == "always" or not have: add( commands, want, "authentication plaintext-password %s" % shlex.quote(want["configured_password"]), ) return commands def parse_full_name(data): match = re.search(r"full-name '(\S+)'", data, re.M) if match: full_name = match.group(1)[1:-1] return full_name def parse_key(data): match = re.search(r"key '(\S+)'", data, re.M) if match: key = match.group(1) return key def parse_key_type(data): match = re.search(r"type '(\S+)'", data, re.M) if match: key_type = match.group(1) return key_type def parse_public_keys(data): """ Parse public keys from the configuration returning dictionary of dictionaries indexed by key name """ match = re.findall(r"public-keys (\S+)", data, re.M) if not match: return dict() keys = dict() for key in set(match): regex = r" %s .+$" % key cfg = re.findall(regex, data, re.M) cfg = "\n".join(cfg) obj = { "name": key, "key": parse_key(cfg), "type": parse_key_type(cfg), } keys[key] = obj return keys def parse_encrypted_password(data): match = re.search(r"authentication encrypted-password '(\S+)'", data, re.M) if match: encrypted_password = match.group(1) return encrypted_password def config_to_dict(module): data = get_config(module) match = re.findall(r"^set system login user (\S+)", data, re.M) if not match: return list() instances = list() for user in set(match): regex = r" %s .+$" % user cfg = re.findall(regex, data, re.M) cfg = "\n".join(cfg) obj = { "name": user, "state": "present", "configured_password": None, "full_name": parse_full_name(cfg), "encrypted_password": parse_encrypted_password(cfg), "public_keys": parse_public_keys(cfg), } instances.append(obj) return instances def get_param_value(key, item, module): # if key doesn't exist in the item, get it from module.params if not item.get(key): value = module.params[key] else: value = item.get(key) # validate the param value (if validator func exists) validator = globals().get("validate_%s" % key) if all((value, validator)): validator(value, module) return value def map_key_params_to_dict(keys): """ Map the list of keys to a dictionary of dictionaries indexed by key name """ all_keys = dict() if keys is None: return all_keys for key in keys: key_name = key["name"] all_keys[key_name] = key return all_keys def map_params_to_obj(module): aggregate = module.params["aggregate"] if not aggregate: if not module.params["name"] and module.params["purge"]: return list() else: users = [{"name": module.params["name"]}] else: users = list() for item in aggregate: if not isinstance(item, dict): users.append({"name": item}) else: users.append(item) objects = list() for item in users: get_value = partial(get_param_value, item=item, module=module) item["configured_password"] = get_value("configured_password") item["encrypted_password"] = get_value("encrypted_password") item["full_name"] = get_value("full_name") item["state"] = get_value("state") item["public_keys"] = map_key_params_to_dict(get_value("public_keys")) objects.append(item) return objects def update_objects(want, have): updates = list() for entry in want: item = next((i for i in have if i["name"] == entry["name"]), None) if item is None: updates.append((entry, {})) elif item: for key, value in entry.items(): if value and value != item[key]: updates.append((entry, item)) return updates def main(): """main entry point for module execution""" public_key_spec = dict( name=dict(required=True, type="str"), key=dict(required=True, type="str", no_log=False), type=dict( required=True, type="str", choices=[ "ssh-dss", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ssh-ed25519", "ecdsa-sha2-nistp521", ], ), ) element_spec = dict( name=dict(), full_name=dict(), configured_password=dict(no_log=True), encrypted_password=dict(no_log=False), update_password=dict(default="always", choices=["on_create", "always"]), state=dict(default="present", choices=["present", "absent"]), public_keys=dict(type="list", elements="dict", options=public_key_spec), ) aggregate_spec = deepcopy(element_spec) aggregate_spec["name"] = dict(required=True) # remove default in aggregate spec, to handle common arguments remove_default_spec(aggregate_spec) argument_spec = dict( aggregate=dict( type="list", elements="dict", options=aggregate_spec, aliases=["users", "collection"], ), purge=dict(type="bool", default=False), ) argument_spec.update(element_spec) mutually_exclusive = [ ("name", "aggregate"), ("encrypted_password", "configured_password"), ] module = AnsibleModule( argument_spec=argument_spec, mutually_exclusive=mutually_exclusive, supports_check_mode=True, ) warnings = list() result = {"changed": False, "warnings": warnings} want = map_params_to_obj(module) have = config_to_dict(module) commands = spec_to_commands(update_objects(want, have), module) if module.params["purge"]: want_users = [x["name"] for x in want] have_users = [x["name"] for x in have] for item in set(have_users).difference(want_users): commands.append("delete system login user %s" % item) result["commands"] = commands if commands: commit = not module.check_mode load_config(module, commands, commit=commit) result["changed"] = True module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/modules/vyos_vrf.py b/plugins/modules/vyos_vrf.py index 395a152b..d0d4fe88 100644 --- a/plugins/modules/vyos_vrf.py +++ b/plugins/modules/vyos_vrf.py @@ -1,1545 +1,1545 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2024 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_vrf """ from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_vrf version_added: 1.0.0 short_description: VRF resource module description: - This module manages vrf configuration on devices running Vyos author: - Evgeny Molotkov (@omnom62) notes: - Tested against vyos 1.4.2 and 1.5-stream-2025-Q1 - This module works with connection C(network_cli). options: config: description: List of vrf configuration. type: dict suboptions: bind_to_all: default: false description: Enable binding services to all VRFs type: bool instances: description: Virtual Routing and Forwarding instance type: list elements: dict suboptions: name: description: VRF instance name required: true type: str description: description: Description type: str disable: default: false description: Administratively disable interface type: bool aliases: ['disabled'] table_id: description: Routing table associated with this instance type: int vni: description: Virtual Network Identifier type: int address_family: type: list elements: dict description: Address family configuration suboptions: afi: description: Address family identifier type: str choices: ['ipv4', 'ipv6'] disable_forwarding: - default: False + default: false description: Disable forwarding for this address family type: bool nht_no_resolve_via_default: - default: False + default: false description: Disable next-hop resolution via default route type: bool route_maps: description: List of route maps for this address family type: list elements: dict suboptions: rm_name: description: Route map name type: str required: true protocol: description: Protocol to which the route map applies type: str choices: - any - babel - bgp - eigrp - isis - ospf - rip - static protocols: # type: list # sanity # elements: dict type: dict description: Protocol configuration suboptions: bgp: type: dict description: BGP configuration suboptions: as_number: description: - AS number. type: int #maximum_paths: --> moved to address-family before 1.3 neighbor: description: BGP neighbor type: list elements: dict suboptions: address: description: - BGP neighbor address (v4/v6). type: str advertisement_interval: description: - Minimum interval for sending routing updates. type: int capability: description: - Advertise capabilities to this neighbor. type: dict suboptions: dynamic: description: - Advertise dynamic capability to this neighbor. type: bool extended_nexthop: description: - Advertise extended nexthop capability to this neighbor. type: bool default_originate: description: - Send default route to this neighbor type: str description: description: - Description of the neighbor type: str disable_capability_negotiation: description: - Disbale capability negotiation with the neighbor type: bool disable_connected_check: description: - Disable check to see if EBGP peer's address is a connected route. type: bool disable_send_community: description: - Disable sending community attributes to this neighbor. type: str choices: ['extended', 'standard'] ebgp_multihop: description: - Allow this EBGP neighbor to not be on a directly connected network. Specify the number hops. type: int local_as: description: local as number not to be prepended to updates from EBGP peers type: int override_capability: description: Ignore capability negotiation with specified neighbor. type: bool passive: description: Do not initiate a session with this neighbor type: bool password: description: BGP MD5 password type: str peer_group_name: description: IPv4 peer group for this peer type: str peer_group: description: True if all the configs under this neighbor key is for peer group template. type: bool port: description: Neighbor's BGP port type: int remote_as: description: Neighbor BGP AS number type: int shutdown: description: Administratively shut down neighbor type: bool solo: # <-- added in 1.3 description: Do not send back prefixes learned from the neighbor type: bool strict_capability_match: description: Enable strict capability negotiation type: bool timers: description: Neighbor timers type: dict suboptions: connect: description: BGP connect timer for this neighbor. type: int holdtime: description: BGP hold timer for this neighbor type: int keepalive: description: BGP keepalive interval for this neighbor type: int ttl_security: description: Number of the maximum number of hops to the BGP peer type: int update_source: description: Source IP of routing updates type: str timers: description: BGP protocol timers type: dict suboptions: keepalive: description: Keepalive interval type: int holdtime: description: Hold time interval type: int bgp_params: description: BGP parameters type: dict suboptions: always_compare_med: description: Always compare MEDs from different neighbors type: bool bestpath: description: Default bestpath selection mechanism type: dict suboptions: as_path: description: AS-path attribute comparison parameters type: str choices: ['confed', 'ignore'] compare_routerid: description: Compare the router-id for identical EBGP paths type: bool med: description: MED attribute comparison parameters type: str choices: ['confed', 'missing-as-worst'] cluster_id: description: Route-reflector cluster-id type: str confederation: description: AS confederation parameters type: list elements: dict suboptions: identifier: description: Confederation AS identifier type: int peers: description: Peer ASs in the BGP confederation type: int dampening: description: Enable route-flap dampening type: dict suboptions: half_life: description: Half-life penalty in seconds type: int max_suppress_time: description: Maximum duration to suppress a stable route type: int re_use: description: Time to start reusing a route type: int start_suppress_time: description: When to start suppressing a route type: int default: description: BGP defaults type: dict suboptions: local_pref: description: Default local preference type: int no_ipv4_unicast: description: | Deactivate IPv4 unicast for a peer by default Deprecated: Unavailable after 1.4 type: bool deterministic_med: description: Compare MEDs between different peers in the same AS type: bool disable_network_import_check: description: Disable IGP route check for network statements type: bool distance: description: Administrative distances for BGP routes type: list elements: dict suboptions: type: description: Type of route type: str choices: ['external', 'internal', 'local'] value: description: distance type: int prefix: description: Administrative distance for a specific BGP prefix type: int enforce_first_as: description: Require first AS in the path to match peer's AS type: bool graceful_restart: description: Maximum time to hold onto restarting peer's stale paths type: int log_neighbor_changes: description: Log neighbor up/down changes and reset reason type: bool no_client_to_client_reflection: description: Disable client to client route reflection type: bool no_fast_external_failover: description: Disable immediate session reset if peer's connected link goes down type: bool router_id: description: BGP router-id type: str scan_time: description: BGP route scanner interval type: int ospf: type: dict description: OSPFv2 configuration suboptions: areas: description: OSPFv2 area. type: list elements: dict suboptions: area_id: description: OSPFv2 area identity. type: str area_type: description: Area type. type: dict suboptions: normal: description: Normal OSPFv2 area. type: bool nssa: description: NSSA OSPFv2 area. type: dict suboptions: set: description: Enabling NSSA. type: bool default_cost: description: Summary-default cost of NSSA area. type: int no_summary: description: Do not inject inter-area routes into stub. type: bool translate: description: NSSA-ABR. type: str choices: [always, candidate, never] stub: description: Stub OSPFv2 area. type: dict suboptions: set: description: Enabling stub. type: bool default_cost: description: Summary-default cost of stub area. type: int no_summary: description: Do not inject inter-area routes into stub. type: bool authentication: description: OSPFv2 area authentication type. type: str choices: [plaintext-password, md5] network: description: OSPFv2 network. type: list elements: dict suboptions: address: required: true description: OSPFv2 IPv4 network address. type: str range: description: Summarize routes matching prefix (border routers only). type: list elements: dict suboptions: address: description: border router IPv4 address. type: str cost: description: Metric for this range. type: int not_advertise: description: Don't advertise this range. type: bool substitute: description: Announce area range (IPv4 address) as another prefix. type: str shortcut: description: Area's shortcut mode. type: str choices: [default, disable, enable] virtual_link: description: Virtual link address. type: list elements: dict suboptions: address: description: virtual link address. type: str authentication: description: OSPFv2 area authentication type. type: dict suboptions: md5: description: MD5 key id based authentication. type: list elements: dict suboptions: key_id: description: MD5 key id. type: int md5_key: description: MD5 key. type: str plaintext_password: description: Plain text password. type: str dead_interval: description: Interval after which a neighbor is declared dead. type: int hello_interval: description: Interval between hello packets. type: int retransmit_interval: description: Interval between retransmitting lost link state advertisements. type: int transmit_delay: description: Link state transmit delay. type: int log_adjacency_changes: description: Log changes in adjacency state. type: str choices: [detail] max_metric: description: OSPFv2 maximum/infinite-distance metric. type: dict suboptions: router_lsa: description: Advertise own Router-LSA with infinite distance (stub router). type: dict suboptions: administrative: description: Administratively apply, for an indefinite period. type: bool on_shutdown: description: Time to advertise self as stub-router. type: int on_startup: description: Time to advertise self as stub-router type: int auto_cost: description: Calculate OSPFv2 interface cost according to bandwidth. type: dict suboptions: reference_bandwidth: description: Reference bandwidth cost in Mbits/sec. type: int default_information: description: Control distribution of default information. type: dict suboptions: originate: description: Distribute a default route. type: dict suboptions: always: description: Always advertise default route. type: bool metric: description: OSPFv2 default metric. type: int metric_type: description: OSPFv2 Metric types for default routes. type: int route_map: description: Route map references. type: str default_metric: description: Metric of redistributed routes type: int distance: description: Administrative distance. type: dict suboptions: global: description: Global OSPFv2 administrative distance. type: int ospf: description: OSPFv2 administrative distance. type: dict suboptions: external: description: Distance for external routes. type: int inter_area: description: Distance for inter-area routes. type: int intra_area: description: Distance for intra-area routes. type: int mpls_te: description: MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters. type: dict suboptions: enabled: description: Enable MPLS-TE functionality. type: bool router_address: description: Stable IP address of the advertising router. type: str neighbor: description: Neighbor IP address. type: list elements: dict suboptions: neighbor_id: description: Identity (number/IP address) of neighbor. type: str poll_interval: description: Seconds between dead neighbor polling interval. type: int priority: description: Neighbor priority. type: int parameters: description: OSPFv2 specific parameters. type: dict suboptions: abr_type: description: OSPFv2 ABR Type. type: str choices: [cisco, ibm, shortcut, standard] opaque_lsa: description: Enable the Opaque-LSA capability (rfc2370). type: bool rfc1583_compatibility: description: Enable rfc1583 criteria for handling AS external routes. type: bool router_id: description: Override the default router identifier. type: str passive_interface: description: Suppress routing updates on an interface. type: list elements: str passive_interface_exclude: description: Interface to exclude when using passive-interface default. type: list elements: str redistribute: description: Redistribute information from another routing protocol. type: list elements: dict suboptions: route_type: description: Route type to redistribute. type: str choices: [bgp, connected, kernel, rip, static] metric: description: Metric for redistribution routes. type: int metric_type: description: OSPFv2 Metric types. type: int route_map: description: Route map references. type: str route_map: description: Filter routes installed in local route map. type: list elements: str timers: description: Adjust routing timers. type: dict suboptions: refresh: description: Adjust refresh parameters. type: dict suboptions: timers: description: refresh timer. type: int throttle: description: Throttling adaptive timers. type: dict suboptions: spf: description: OSPFv2 SPF timers. type: dict suboptions: delay: description: Delay (msec) from first change received till SPF calculation. type: int initial_holdtime: description: Initial hold time(msec) between consecutive SPF calculations. type: int max_holdtime: description: maximum hold time (sec). type: int ospfv3: type: dict description: OSPFv3 configuration suboptions: areas: description: OSPFv3 area. type: list elements: dict suboptions: area_id: description: OSPFv3 Area name/identity. type: str export_list: description: Name of export-list. type: str import_list: description: Name of import-list. type: str interface: description: Enable OSPVv3 on an interface for this area. aliases: ['interfaces'] type: list elements: dict suboptions: name: description: Interface name. type: str range: description: Summarize routes matching prefix (border routers only). type: list elements: dict suboptions: address: description: border router IPv4 address. type: str advertise: description: Advertise this range. type: bool not_advertise: description: Don't advertise this range. type: bool parameters: description: OSPFv3 specific parameters. type: dict suboptions: router_id: description: Override the default router identifier. type: str redistribute: description: Redistribute information from another routing protocol. type: list elements: dict suboptions: route_type: description: Route type to redistribute. type: str choices: - bgp - connected - kernel - ripng - static route_map: description: Route map references. type: str static: type: list description: Static routes configuration elements: dict suboptions: address_families: description: A dictionary specifying the address family to which the static route(s) belong. type: list elements: dict suboptions: afi: description: - Specifies the type of route. type: str choices: - ipv4 - ipv6 required: true routes: description: A dictionary that specify the static route configurations. type: list elements: dict suboptions: dest: description: - An IPv4/v6 address in CIDR notation that specifies the destination network for the static route. type: str required: true blackhole_config: description: - Configured to silently discard packets. type: dict suboptions: type: description: - This is to configure only blackhole. type: str distance: description: - Distance for the route. type: int next_hops: description: - Next hops to the specified destination. type: list elements: dict suboptions: forward_router_address: description: - The IP address of the next hop that can be used to reach the destination network. type: str enabled: description: - Disable IPv4/v6 next-hop static route. type: bool admin_distance: description: - Distance value for the route. type: int interface: description: - Name of the outgoing interface. type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VYOS device by executing the command B(show configuration commands | match "set vrf"). - The states I(replaced) and I(overridden) have identical behaviour for this module. - The state I(parsed) reads the configuration from C(show configuration commands | match "set vrf") option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in. type: str choices: - deleted - merged - overridden - replaced - gathered - rendered - parsed default: merged """ EXAMPLES = """ # # ------------------- # # 1. Using merged # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue disable # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # vyos@vyos:~$ # # Task # # ------------- # - name: Merge provided configuration with device configuration # vyos.vyos.vyos_vrf: # config: # instances: # - name: "vrf-green" # description: "green-vrf" # table_id: 110 # vni: 1010 # Task output: # ------------- # "after": { # "bind_to_all": false, # "instances": [ # { # "description": "blue-vrf", # "disable": true, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # }, # { # "description": "green-vrf", # "disable": false, # "name": "vrf-green", # "table_id": 110, # "vni": 1010 # } # ] # }, # "before": { # "bind_to_all": false, # "instances": [ # { # "description": "blue-vrf", # "disable": true, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # } # ] # }, # "changed": true, # "commands": [ # "set vrf name vrf-green table 110", # "set vrf name vrf-green vni 1010", # "set vrf name vrf-green description green-vrf" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue disable # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-green description 'green-vrf' # set vrf name vrf-green table '110' # set vrf name vrf-green vni '1010' # vyos@vyos:~$ # # ------------------- # # 2. Using replaced # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # # Task # # ------------- # - name: Merge provided configuration with device configuration # vyos.vyos.vyos_vrf: # config: # bind_to_all: true # instances: # - name: "vrf-blue" # description: "blue-vrf" # disable: false # table_id: 100 # vni: 1002 # - name: "vrf-red" # description: "red-vrf" # disable: false # table_id: 101 # vni: 1001 # address_family: # - afi: "ipv4" # disable_forwarding: false # route_maps: # - rm_name: "rm1" # protocol: "ospf" # - afi: "ipv6" # nht_no_resolve_via_default: true # state: replaced # # Task output: # # ------------- # "after": { # "bind_to_all": true, # "instances": [ # { # "description": "blue-vrf", # "disable": false, # "name": "vrf-blue", # "table_id": 100, # "vni": 1002 # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": false, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "ospf", # "rm_name": "rm1" # }, # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # }, # { # "afi": "ipv6", # "disable_forwarding": false, # "nht_no_resolve_via_default": true # } # ], # "description": "red-vrf", # "disable": false, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # }, # "before": { # "bind_to_all": true, # "instances": [ # { # "description": "blue-vrf", # "disable": false, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": true, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # } # ], # "description": "red-vrf", # "disable": true, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # }, # "changed": true, # "commands": [ # "set vrf name vrf-blue vni 1002", # "delete vrf name vrf-red disable", # "set vrf name vrf-red ip protocol ospf route-map rm1", # "delete vrf name vrf-red ip disable-forwarding", # "set vrf name vrf-red ipv6 nht no-resolve-via-default" # ] # After state: # # ------------- # vyos@vyos:~$ # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1002' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red ip protocol ospf route-map 'rm1' # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red ipv6 nht no-resolve-via-default # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # # ------------------- # # 3. Using overridden # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # Task # ------------- # - name: Overridden provided configuration with device configuration # vyos.vyos.vyos_vrf: # config: # bind_to_all: true # instances: # - name: "vrf-blue" # description: "blue-vrf" # disable: true # table_id: 100 # vni: 1000 # - name: "vrf-red" # description: "red-vrf" # disable: true # table_id: 101 # vni: 1001 # address_family: # - afi: "ipv4" # disable_forwarding: false # route_maps: # - rm_name: "rm1" # protocol: "rip" # - afi: "ipv6" # nht_no_resolve_via_default: false # state: overridden # # Task output: # # ------------- # "after": { # "bind_to_all": true, # "instances": [ # { # "description": "blue-vrf", # "disable": true, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": false, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # } # ], # "description": "red-vrf", # "disable": true, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # }, # "before": { # "bind_to_all": true, # "instances": [ # { # "description": "blue-vrf", # "disable": false, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": true, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # } # ], # "description": "red-vrf", # "disable": true, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # }, # "changed": true, # "commands": [ # "delete vrf name vrf-blue", # "commit", # "delete vrf name vrf-red", # "commit", # "set vrf name vrf-blue table 100", # "set vrf name vrf-blue vni 1000", # "set vrf name vrf-blue description blue-vrf", # "set vrf name vrf-blue disable", # "set vrf name vrf-red table 101", # "set vrf name vrf-red vni 1001", # "set vrf name vrf-red description red-vrf", # "set vrf name vrf-red disable", # "set vrf name vrf-red ip protocol rip route-map rm1" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue disable # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # 4. Using gathered # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # Task # ------------- # - name: Gather provided configuration with device configuration # vyos.vyos.vyos_vrf: # config: # state: gathered # # Task output: # # ------------- # "gathered": { # "bind_to_all": true, # "instances": [ # { # "description": "blue-vrf", # "disable": false, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": true, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # } # ], # "description": "red-vrf", # "disable": true, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # } # After state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # # ------------------- # # 5. Using deleted # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf bind-to-all # set vrf name vrf-blue description 'blue-vrf' # set vrf name vrf-blue table '100' # set vrf name vrf-blue vni '1000' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # # Task # # ------------- # - name: Replace provided configuration with device configuration # vyos.vyos.vyos_vrf: # config: # bind_to_all: false # instances: # - name: "vrf-blue" # state: deleted # # Task output: # # ------------- # "after": { # "bind_to_all": false, # "instances": [ # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": true, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # } # ], # "description": "red-vrf", # "disable": true, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # }, # "before": { # "bind_to_all": true, # "instances": [ # { # "description": "blue-vrf", # "disable": false, # "name": "vrf-blue", # "table_id": 100, # "vni": 1000 # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": true, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "rip", # "rm_name": "rm1" # } # ] # } # ], # "description": "red-vrf", # "disable": true, # "name": "vrf-red", # "table_id": 101, # "vni": 1001 # } # ] # }, # "changed": true, # "commands": [ # "delete vrf bind-to-all", # "delete vrf name vrf-blue" # ] # After state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # # ------------------- # # 6. Using rendered # # ------------------- # # Before state: # # ------------- # vyos@vyos:~$ show configuration commands | match 'set vrf' # set vrf name vrf-red description 'red-vrf' # set vrf name vrf-red disable # set vrf name vrf-red ip disable-forwarding # set vrf name vrf-red ip protocol rip route-map 'rm1' # set vrf name vrf-red table '101' # set vrf name vrf-red vni '1001' # vyos@vyos:~$ # Task # ------------- # - name: Render provided configuration with device configuration # vyos.vyos.vyos_vrf: # config: # bind_to_all: true # instances: # - name: "vrf-green" # description: "green-vrf" # disabled: true # table_id: 105 # vni: 1000 # - name: "vrf-amber" # description: "amber-vrf" # disable: false # table_id: 111 # vni: 1001 # address_family: # - afi: "ipv4" # disable_forwarding: true # route_maps: # - rm_name: "rm1" # protocol: "ospf" # - afi: "ipv6" # nht_no_resolve_via_default: false # state: rendered # # Task output: # # ------------- # "rendered": [ # "set vrf bind-to-all", # "set vrf name vrf-green table 105", # "set vrf name vrf-green vni 1000", # "set vrf name vrf-green description green-vrf", # "set vrf name vrf-green disable", # "set vrf name vrf-amber table 111", # "set vrf name vrf-amber vni 1001", # "set vrf name vrf-amber description amber-vrf", # "set vrf name vrf-amber ip protocol ospf route-map rm1", # "set vrf name vrf-amber ip disable-forwarding" # ] # # ------------------- # # 7. Using parsed # # ------------------- # # vrf_parsed.cfg: # # ------------- # set vrf bind-to-all # set vrf name vrf1 description 'red' # set vrf name vrf1 disable # set vrf name vrf1 table 101 # set vrf name vrf1 vni 501 # set vrf name vrf2 description 'blah2' # set vrf name vrf2 disable # set vrf name vrf2 table 102 # set vrf name vrf2 vni 102 # set vrf name vrf1 ip disable-forwarding # set vrf name vrf1 ip nht no-resolve-via-default # set vrf name vrf-red ip protocol ospf route-map 'rm1' # set vrf name vrf-red ipv6 nht no-resolve-via-default # Task: # ------------- # - name: Parse provided configuration with device configuration # vyos.vyos.vyos_vrf: # running_config: "{{ lookup('file', './vrf_parsed.cfg') }}" # state: parsed # # Task output: # # ------------- # "parsed": { # "bind_to_all": true, # "instances": [ # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": true, # "nht_no_resolve_via_default": true # } # ], # "description": "red", # "disable": true, # "name": "vrf1" # }, # { # "description": "blah2", # "disable": true, # "name": "vrf2" # }, # { # "address_family": [ # { # "afi": "ipv4", # "disable_forwarding": false, # "nht_no_resolve_via_default": false, # "route_maps": [ # { # "protocol": "ospf", # "rm_name": "rm1" # } # ] # }, # { # "afi": "ipv6", # "disable_forwarding": false, # "nht_no_resolve_via_default": true # } # ], # "disable": false, # "name": "vrf-red" # } # ] # } """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: list sample: - set system ntp server server1 dynamic - set system ntp server server1 prefer - set system ntp server server2 noselect - set system ntp server server2 preempt - set system ntp server server_add preempt rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - set system ntp server server1 dynamic - set system ntp server server1 prefer - set system ntp server server2 noselect - set system ntp server server2 preempt - set system ntp server server_add preempt gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.vrf.vrf import VrfArgs from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.vrf.vrf import Vrf def main(): """ Main entry point for module execution :returns: the result form module invocation """ module = AnsibleModule( argument_spec=VrfArgs.argument_spec, mutually_exclusive=[["config", "running_config"]], required_if=[ ["state", "merged", ["config"]], ["state", "replaced", ["config"]], ["state", "overridden", ["config"]], ["state", "rendered", ["config"]], ["state", "parsed", ["running_config"]], ], supports_check_mode=True, ) result = Vrf(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main()