diff options
10 files changed, 422 insertions, 29 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py index 91434e4..2f1dfe4 100644 --- a/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py @@ -25,7 +25,6 @@ The arg spec for the vyos_l3_interfaces module """ - from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -73,8 +72,17 @@ class L3_interfacesArgs(object): # pylint: disable=R0903 }, "type": "list", }, + "running_config": {"type": "str"}, "state": { - "choices": ["merged", "replaced", "overridden", "deleted"], + "choices": [ + "merged", + "replaced", + "overridden", + "deleted", + "rendered", + "gathered", + "parsed", + ], "default": "merged", "type": "str", }, diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py index a23e417..122cc1e 100644 --- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py @@ -52,14 +52,14 @@ class L3_interfaces(ConfigBase): def __init__(self, module): super(L3_interfaces, self).__init__(module) - def get_l3_interfaces_facts(self): + def get_l3_interfaces_facts(self, data=None): """ Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources + self.gather_subset, self.gather_network_resources, data=data ) l3_interfaces_facts = facts["ansible_network_resources"].get( "l3_interfaces" @@ -78,25 +78,44 @@ class L3_interfaces(ConfigBase): warnings = list() commands = list() - existing_l3_interfaces_facts = self.get_l3_interfaces_facts() - commands.extend(self.set_config(existing_l3_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 + if self.state in self.ACTION_STATES: + existing_l3_interfaces_facts = self.get_l3_interfaces_facts() + else: + existing_l3_interfaces_facts = [] - result["commands"] = commands + if self.state in self.ACTION_STATES or self.state == "rendered": + commands.extend(self.set_config(existing_l3_interfaces_facts)) - if self._module._diff: - result["diff"] = resp["diff"] if result["changed"] else None + if commands and self.state in self.ACTION_STATES: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True - changed_l3_interfaces_facts = self.get_l3_interfaces_facts() + if self.state in self.ACTION_STATES: + result["commands"] = commands + + if self.state in self.ACTION_STATES or self.state == "gathered": + changed_l3_interfaces_facts = self.get_l3_interfaces_facts() + elif self.state == "rendered": + result["rendered"] = commands + elif self.state == "parsed": + running_config = self._module.params["running_config"] + if not running_config: + self._module.fail_json( + msg="value of running_config parameter must not be empty for state parsed" + ) + result["parsed"] = self.get_l3_interfaces_facts( + data=running_config + ) + else: + changed_l3_interfaces_facts = [] - result["before"] = existing_l3_interfaces_facts - if result["changed"]: - result["after"] = changed_l3_interfaces_facts + if self.state in self.ACTION_STATES: + result["before"] = existing_l3_interfaces_facts + if result["changed"]: + result["after"] = changed_l3_interfaces_facts + elif self.state == "gathered": + result["gathered"] = changed_l3_interfaces_facts result["warnings"] = warnings return result @@ -126,7 +145,10 @@ class L3_interfaces(ConfigBase): commands = [] state = self._module.params["state"] - if state in ("merged", "replaced", "overridden") and not want: + if ( + state in ("merged", "replaced", "overridden", "rendered") + and not want + ): self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format( state @@ -154,7 +176,7 @@ class L3_interfaces(ConfigBase): if not obj_in_have: obj_in_have = {"name": item["name"]} - if state == "merged": + if state in ("merged", "rendered"): commands.extend(self._state_merged(item, obj_in_have)) elif state == "replaced": diff --git a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py index d1d62c2..3b99d34 100644 --- a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py @@ -10,7 +10,12 @@ for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from __future__ import absolute_import, division, print_function +from __future__ import ( + absolute_import, + division, + print_function, + unicode_literals, +) __metaclass__ = type diff --git a/plugins/modules/vyos_l3_interfaces.py b/plugins/modules/vyos_l3_interfaces.py index a77ecaf..4724240 100644 --- a/plugins/modules/vyos_l3_interfaces.py +++ b/plugins/modules/vyos_l3_interfaces.py @@ -37,12 +37,14 @@ ANSIBLE_METADATA = { } DOCUMENTATION = """module: vyos_l3_interfaces -short_description: Manages L3 interface attributes of VyOS network devices. +short_description: L3 interfaces resource module description: This module manages the L3 interface attributes 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: Nilashish Chakraborty (@NilashishC) +author: +- Nilashish Chakraborty (@NilashishC) +- Rohit Thakur (@rohitthakur2590) options: config: description: The provided L3 interfaces configuration. @@ -104,6 +106,16 @@ options: description: - IPv6 address of the virtual interface. type: str + running_config: + description: + - This option is used only with state I(parsed). + - The value of this option should be the output received from the VyOS device by executing + the command B(show configuration commands | grep -e eth[2,3]). + - The state I(parsed) reads the configuration from C(running_config) option and transforms + it into Ansible structured data as per the resource module's argspec and the value is then + returned in the I(parsed) key within the result. + type: str + version_added: "1.0.0" state: description: - The state of the configuration after module completion. @@ -113,6 +125,9 @@ options: - replaced - overridden - deleted + - parsed + - gathered + - rendered default: merged """ EXAMPLES = """ @@ -128,7 +143,7 @@ EXAMPLES = """ # set interfaces ethernet eth3 vif 102 - name: Merge provided configuration with device configuration - vyos_l3_interfaces: + vyos.vyos.vyos_l3_interfaces: config: - name: eth2 ipv4: @@ -196,7 +211,7 @@ EXAMPLES = """ # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' # - name: Replace device configurations of listed interfaces with provided configurations - vyos_l3_interfaces: + vyos.vyos.vyos_l3_interfaces: config: - name: eth2 ipv4: @@ -252,7 +267,7 @@ EXAMPLES = """ # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' - name: Overrides all device configuration with provided configuration - vyos_l3_interfaces: + vyos.vyos.vyos_l3_interfaces: config: - name: eth0 ipv4: @@ -303,7 +318,7 @@ EXAMPLES = """ # set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34' - name: Delete L3 attributes of given interfaces (Note - This won't delete the interface itself) - vyos_l3_interfaces: + vyos.vyos.vyos_l3_interfaces: config: - name: eth1 - name: eth2 @@ -326,6 +341,180 @@ EXAMPLES = """ # set interfaces ethernet eth3 smp_affinity 'auto' +# Using gathered +# +# Before state: +# ------------- +# +# vyos:~$ show configuration commands | grep -e eth[2,3,0] +# set interfaces ethernet eth0 address 'dhcp' +# set interfaces ethernet eth0 duplex 'auto' +# set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' +# set interfaces ethernet eth0 smp_affinity 'auto' +# set interfaces ethernet eth0 speed 'auto' +# set interfaces ethernet eth1 address '192.0.2.14/24' +# set interfaces ethernet eth2 address '192.0.2.11/24' +# set interfaces ethernet eth2 address '192.0.2.10/24' +# set interfaces ethernet eth2 address '2001:db8::10/32' +# set interfaces ethernet eth2 address '2001:db8::12/32' +# +- name: Gather listed l3 interfaces with provided configurations + vyos.vyos.vyos_l3_interfaces: + config: + state: gathered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "gathered": [ +# { +# "ipv4": [ +# { +# "address": "192.0.2.11/24" +# }, +# { +# "address": "192.0.2.10/24" +# } +# ], +# "ipv6": [ +# { +# "address": "2001:db8::10/32" +# }, +# { +# "address": "2001:db8::12/32" +# } +# ], +# "name": "eth2" +# }, +# { +# "ipv4": [ +# { +# "address": "192.0.2.14/24" +# } +# ], +# "name": "eth1" +# }, +# { +# "ipv4": [ +# { +# "address": "dhcp" +# } +# ], +# "name": "eth0" +# } +# ] +# +# +# After state: +# ------------- +# +# vyos:~$ show configuration commands | grep -e eth[2,3] +# set interfaces ethernet eth0 address 'dhcp' +# set interfaces ethernet eth0 duplex 'auto' +# set interfaces ethernet eth0 hw-id '08:00:27:50:5e:19' +# set interfaces ethernet eth0 smp_affinity 'auto' +# set interfaces ethernet eth0 speed 'auto' +# set interfaces ethernet eth1 address '192.0.2.14/24' +# set interfaces ethernet eth2 address '192.0.2.11/24' +# set interfaces ethernet eth2 address '192.0.2.10/24' +# set interfaces ethernet eth2 address '2001:db8::10/32' +# set interfaces ethernet eth2 address '2001:db8::12/32' + + +# Using rendered +# +# +- name: Render the commands for provided configuration + vyos.vyos.vyos_l3_interfaces: + config: + - name: eth1 + ipv4: + - address: 192.0.2.14/24 + - name: eth2 + ipv4: + - address: 192.0.2.10/24 + - address: 192.0.2.11/24 + ipv6: + - address: 2001:db8::10/32 + - address: 2001:db8::12/32 + state: rendered +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "rendered": [ +# "set interfaces ethernet eth1 address '192.0.2.14/24'", +# "set interfaces ethernet eth2 address '192.0.2.11/24'", +# "set interfaces ethernet eth2 address '192.0.2.10/24'", +# "set interfaces ethernet eth2 address '2001:db8::10/32'", +# "set interfaces ethernet eth2 address '2001:db8::12/32'" +# ] + + +# Using parsed +# +# +- name: parse the provided running configuration + vyos.vyos.vyos_l3_interfaces: + running_config: + "set interfaces ethernet eth0 address 'dhcp' + set interfaces ethernet eth1 address '192.0.2.14/24' + set interfaces ethernet eth2 address '192.0.2.10/24' + set interfaces ethernet eth2 address '192.0.2.11/24' + set interfaces ethernet eth2 address '2001:db8::10/32' + set interfaces ethernet eth2 address '2001:db8::12/32'" + state: parsed +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# +# "parsed": [ +# { +# "ipv4": [ +# { +# "address": "192.0.2.10/24" +# }, +# { +# "address": "192.0.2.11/24" +# } +# ], +# "ipv6": [ +# { +# "address": "2001:db8::10/32" +# }, +# { +# "address": "2001:db8::12/32" +# } +# ], +# "name": "eth2" +# }, +# { +# "ipv4": [ +# { +# "address": "192.0.2.14/24" +# } +# ], +# "name": "eth1" +# }, +# { +# "ipv4": [ +# { +# "address": "dhcp" +# } +# ], +# "name": "eth0" +# } +# ] + + """ RETURN = """ before: @@ -365,8 +554,20 @@ def main(): :returns: the result form module invocation """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "rendered", ("config",)), + ("state", "overridden", ("config",)), + ("state", "parsed", ("running_config",)), + ] + mutually_exclusive = [("config", "running_config")] + module = AnsibleModule( - argument_spec=L3_interfacesArgs.argument_spec, supports_check_mode=True + argument_spec=L3_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + mutually_exclusive=mutually_exclusive, ) result = L3_interfaces(module).execute_module() diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg new file mode 100644 index 0000000..bc70f1c --- /dev/null +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg @@ -0,0 +1,7 @@ +set interfaces ethernet eth0 address 'dhcp' +set interfaces ethernet eth1 address '192.0.2.14/24' +set interfaces ethernet eth2 address '192.0.2.10/24' +set interfaces ethernet eth2 address '192.0.2.11/24' +set interfaces ethernet eth2 address '2001:db8::10/32' +set interfaces ethernet eth2 address '2001:db8::12/32' + diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/empty_config.yaml index 96d4cda..9929dd7 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/empty_config.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/empty_config.yaml @@ -35,3 +35,26 @@ - assert: that: - result.msg == 'value of config parameter must not be empty for state overridden' + +- name: Parsed with empty running_config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_l3_interfaces: + running_config: + state: parsed + +- assert: + that: + - result.msg == 'value of running_config parameter must not be empty for state + parsed' + +- name: Rendered with empty config should give appropriate error message + register: result + ignore_errors: true + vyos.vyos.vyos_l3_interfaces: + config: + state: rendered + +- assert: + that: + - result.msg == 'value of config parameter must not be empty for state rendered' diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml new file mode 100644 index 0000000..625047b --- /dev/null +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml @@ -0,0 +1,34 @@ +--- +- debug: + msg: START vyos_l3_interfaces gathered integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Merge the provided configuration with the exisiting running configuration + register: result + vyos.vyos.vyos_l3_interfaces: &id001 + config: + state: gathered + + - name: Assert that gathered dicts was correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['gathered']) |length == 0\ + \ }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + register: result + vyos.vyos.vyos_l3_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/parsed.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/parsed.yaml new file mode 100644 index 0000000..2e0ce13 --- /dev/null +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/parsed.yaml @@ -0,0 +1,41 @@ +--- +- debug: + msg: START vyos_l3_interfaces parsed integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Gather l3_interfaces facts + register: l3_interfaces_facts + vyos.vyos.vyos_facts: + gather_subset: + - default + gather_network_resources: + - l3_interfaces + + - name: Provide the running configuration for parsing (config to be parsed) + register: result + vyos.vyos.vyos_l3_interfaces: &id001 + running_config: "{{ lookup('file', '_parsed_config.cfg') }}" + state: parsed + + - name: Assert that correct parsing done + assert: + that: "{{ ansible_facts['network_resources']['l3_interfaces'] | symmetric_difference(result['parsed'])\ + \ |length == 0 }}" + + - name: Gather the existing running configuration (IDEMPOTENT) + register: result + vyos.vyos.vyos_l3_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml new file mode 100644 index 0000000..02a2865 --- /dev/null +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml @@ -0,0 +1,44 @@ +--- +- debug: + msg: START vyos_l3_interfaces rendered integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + + - name: Structure provided configuration into device specific commands + register: result + vyos.vyos.vyos_l3_interfaces: &id001 + config: + - name: eth1 + ipv4: + - address: 192.0.2.14/24 + - name: eth2 + ipv4: + - address: 192.0.2.10/24 + - address: 192.0.2.11/24 + ipv6: + - address: 2001:db8::10/32 + - address: 2001:db8::12/32 + state: rendered + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ rendered['commands'] | symmetric_difference(result['rendered'])\ + \ |length == 0 }}" + + - name: Structure provided configuration into device specific commands (IDEMPOTENT) + register: result + vyos.vyos.vyos_l3_interfaces: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + always: + + - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml b/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml index ee329d3..54be4c0 100644 --- a/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml @@ -79,6 +79,14 @@ overridden: ipv4: - address: 192.0.2.15/24 - name: eth2 +rendered: + commands: + - set interfaces ethernet eth1 address '192.0.2.14/24' + - set interfaces ethernet eth2 address '192.0.2.11/24' + - set interfaces ethernet eth2 address '192.0.2.10/24' + - set interfaces ethernet eth2 address '2001:db8::10/32' + - set interfaces ethernet eth2 address '2001:db8::12/32' + deleted: commands: - delete interfaces ethernet eth1 address '192.0.2.14/24' |