diff options
10 files changed, 415 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 91434e4b..2f1dfe45 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 a23e417f..122cc1eb 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 d1d62c23..3b99d347 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 a77ecaf7..47242405 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 00000000..ef070a72 --- /dev/null +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg @@ -0,0 +1,9 @@ +set interfaces ethernet eth1 address '192.0.2.10/24' +set interfaces ethernet eth1 address '2001:db8::10/32' +set interfaces ethernet eth1 hw-id '08:00:27:da:67:43' +set interfaces ethernet eth2 address '198.51.100.10/24' +set interfaces ethernet eth2 hw-id '08:00:27:d8:70:b0' +set interfaces ethernet eth2 vif 101 address '198.51.100.130/25' +set interfaces ethernet eth2 vif 101 address '2001:db8::20/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 96d4cda9..9929dd7f 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 00000000..625047bb --- /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 00000000..d5ff2056 --- /dev/null +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/parsed.yaml @@ -0,0 +1,16 @@ +--- +- debug: +    msg: START vyos_l3_nterfaces parsed integration tests on connection={{ ansible_connection +      }} + +- name: Parse externally provided interfaces config to agnostic model +  register: result +  vyos.vyos.vyos_l3_interfaces: +    running_config: "{{ lookup('file', '_parsed_config.cfg') }}" +    state: parsed + +- name: Assert that config was correctly parsed +  assert: +    that: +      - "{{ parsed['after'] | symmetric_difference(result['parsed']) |length ==\ +        \ 0 }}" 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 00000000..02a28655 --- /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 ee329d30..e9781e6a 100644 --- a/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml @@ -79,6 +79,30 @@ overridden:        ipv4:          - address: 192.0.2.15/24      - name: eth2 +parsed: +  after: +    - name: eth1 +      ipv4: +        - address: 192.0.2.10/24 +      ipv6: +        - address: 2001:db8::10/32 +    - name: eth2 +      ipv4: +        - address: 198.51.100.10/24 +      vifs: +        - vlan_id: 101 +          ipv4: +            - address: 198.51.100.130/25 +          ipv6: +            - address: 2001:db8::20/32 +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'  | 
