From 5ab8d30d108f8ac76000c43ac3c69129082b9458 Mon Sep 17 00:00:00 2001 From: Ganesh Nalawade Date: Thu, 20 Jul 2017 11:20:18 +0530 Subject: [PATCH] Add net_vrf implementation for junos (#27055) * junos_vrf implementation * junos_vrf integration test * net_vrf integration test for junos --- .../modules/network/junos/junos_vrf.py | 234 ++++++++++++++++++ test/integration/junos.yaml | 10 + .../targets/junos_vrf/defaults/main.yaml | 3 + .../targets/junos_vrf/tasks/main.yaml | 2 + .../targets/junos_vrf/tasks/netconf.yaml | 14 ++ .../junos_vrf/tests/netconf/basic.yaml | 171 +++++++++++++ .../targets/net_vrf/tasks/main.yaml | 1 + .../targets/net_vrf/tasks/netconf.yaml | 16 ++ .../targets/net_vrf/tests/junos/basic.yaml | 171 +++++++++++++ .../targets/net_vrf/tests/netconf/basic.yaml | 3 + 10 files changed, 625 insertions(+) create mode 100644 lib/ansible/modules/network/junos/junos_vrf.py create mode 100644 test/integration/targets/junos_vrf/defaults/main.yaml create mode 100644 test/integration/targets/junos_vrf/tasks/main.yaml create mode 100644 test/integration/targets/junos_vrf/tasks/netconf.yaml create mode 100644 test/integration/targets/junos_vrf/tests/netconf/basic.yaml create mode 100644 test/integration/targets/net_vrf/tasks/netconf.yaml create mode 100644 test/integration/targets/net_vrf/tests/junos/basic.yaml create mode 100644 test/integration/targets/net_vrf/tests/netconf/basic.yaml diff --git a/lib/ansible/modules/network/junos/junos_vrf.py b/lib/ansible/modules/network/junos/junos_vrf.py new file mode 100644 index 00000000000..d3e0b663e6a --- /dev/null +++ b/lib/ansible/modules/network/junos/junos_vrf.py @@ -0,0 +1,234 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (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 . +# + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'core'} + +DOCUMENTATION = """ +--- +module: junos_vrf +version_added: "2.4" +author: "Ganesh Nalawade (@ganeshrn)" +short_description: Manage the VRF definitions on Juniper JUNOS devices +description: + - This module provides declarative management of VRF definitions on + Juniper JUNOS devices. It allows playbooks to manage individual or + the entire VRF collection. +options: + name: + description: + - The name of the VRF definition to be managed on the remote IOS + device. The VRF definition name is an ASCII string name used + to uniquely identify the VRF. This argument is mutually exclusive + with the C(aggregate) argument + description: + description: + - Provides a short description of the VRF definition in the + current active configuration. The VRF definition value accepts + alphanumeric characters used to provide additional information + about the VRF. + rd: + description: + - The router-distinguisher value uniquely identifies the VRF to + routing processes on the remote IOS system. The RD value takes + the form of C(A:B) where C(A) and C(B) are both numeric values. + interfaces: + description: + - Identifies the set of interfaces that + should be configured in the VRF. Interfaces must be routed + interfaces in order to be placed into a VRF. + target: + description: + - It configures VRF target community configuration. The target value takes + the form of C(target:A:B) where C(A) and C(B) are both numeric values. + aggregate: + description: + - The set of VRF definition objects to be configured on the remote + JUNOS device. Ths list entries can either be the VRF name or a hash + of VRF definitions and attributes. This argument is mutually + exclusive with the C(name) argument. + purge: + description: + - Instructs the module to consider the + VRF definition absolute. It will remove any previously configured + VRFs on the device. + default: false + state: + description: + - Configures the state of the VRF definition + as it relates to the device operational configuration. When set + to I(present), the VRF should be configured in the device active + configuration and when set to I(absent) the VRF should not be + in the device active configuration + default: present + choices: ['present', 'absent'] + active: + description: + - Specifies whether or not the configuration is active or deactivated + default: True + choices: [True, False] +requirements: + - ncclient (>=v0.5.2) +notes: + - This module requires the netconf system service be enabled on + the remote device being managed +""" +EXAMPLES = """ +- name: Configure vrf configuration + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + +- name: Remove vrf configuration + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: absent + +- name: Deactivate vrf configuration + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + active: False + +- name: Activate vrf configuration + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + active: True +""" + +RETURN = """ +diff.prepared: + description: Configuration difference before and after applying change. + returned: when configuration is changed and diff option is enabled. + type: string + sample: > + [edit routing-instances] + + test-1 { + + description test-vrf-1; + + instance-type vrf; + + interface ge-0/0/2.0; + + interface ge-0/0/3.0; + + route-distinguisher 1.1.1.1:10; + + vrf-target target:65514:113; + + } +""" +import collections + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.junos import junos_argument_spec, check_args +from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele +from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config + +try: + from lxml.etree import tostring +except ImportError: + from xml.etree.ElementTree import tostring + +USE_PERSISTENT_CONNECTION = True + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + name=dict(required=True), + description=dict(), + rd=dict(type='list'), + interfaces=dict(type='list'), + target=dict(type='list'), + aggregate=dict(type='list'), + purge=dict(default=False, type='bool'), + state=dict(default='present', choices=['present', 'absent']), + active=dict(default=True, type='bool') + ) + + argument_spec.update(junos_argument_spec) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True) + + warnings = list() + check_args(module, warnings) + + result = {'changed': False} + + if warnings: + result['warnings'] = warnings + + top = 'routing-instances/instance' + + param_to_xpath_map = collections.OrderedDict() + param_to_xpath_map.update([ + ('name', {'xpath': 'name', 'is_key': True}), + ('description', 'description'), + ('type', 'instance-type'), + ('rd', 'route-distinguisher/rd-type'), + ('interfaces', 'interface/name'), + ('target', 'vrf-target/community'), + ]) + + module.params['type'] = 'vrf' + + want = map_params_to_obj(module, param_to_xpath_map) + ele = map_obj_to_ele(module, want, top) + + with locked_config(module): + diff = load_config(module, tostring(ele), warnings, action='replace') + + commit = not module.check_mode + if diff: + if commit: + commit_configuration(module) + else: + discard_changes(module) + result['changed'] = True + + if module._diff: + result['diff'] = {'prepared': diff} + + module.exit_json(**result) + +if __name__ == "__main__": + main() diff --git a/test/integration/junos.yaml b/test/integration/junos.yaml index c16d6942a7a..1cf8ed000a2 100644 --- a/test/integration/junos.yaml +++ b/test/integration/junos.yaml @@ -165,6 +165,16 @@ - set_fact: failed_modules: "{{ failed_modules }} + [ 'junos_lldp_interface' ]" test_failed: true + + - block: + - include_role: + name: junos_vrf + when: "limit_to in ['*', 'junos_vrf']" + rescue: + - set_fact: + failed_modules: "{{ failed_modules }} + [ 'junos_vrf' ]" + test_failed: true + ########### - debug: var=failed_modules when: test_failed diff --git a/test/integration/targets/junos_vrf/defaults/main.yaml b/test/integration/targets/junos_vrf/defaults/main.yaml new file mode 100644 index 00000000000..9ef5ba51651 --- /dev/null +++ b/test/integration/targets/junos_vrf/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "*" +test_items: [] diff --git a/test/integration/targets/junos_vrf/tasks/main.yaml b/test/integration/targets/junos_vrf/tasks/main.yaml new file mode 100644 index 00000000000..cc27f174fd8 --- /dev/null +++ b/test/integration/targets/junos_vrf/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: netconf.yaml, tags: ['netconf'] } diff --git a/test/integration/targets/junos_vrf/tasks/netconf.yaml b/test/integration/targets/junos_vrf/tasks/netconf.yaml new file mode 100644 index 00000000000..c6a07db9a63 --- /dev/null +++ b/test/integration/targets/junos_vrf/tasks/netconf.yaml @@ -0,0 +1,14 @@ +- name: collect netconf test cases + find: + paths: "{{ role_path }}/tests/netconf" + patterns: "{{ testcase }}.yaml" + register: test_cases + +- name: set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: run test case + include: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/junos_vrf/tests/netconf/basic.yaml b/test/integration/targets/junos_vrf/tests/netconf/basic.yaml new file mode 100644 index 00000000000..32850112fcf --- /dev/null +++ b/test/integration/targets/junos_vrf/tests/netconf/basic.yaml @@ -0,0 +1,171 @@ +--- +- debug: msg="START junos_vrf netconf/basic.yaml" + +- name: setup - remove vrf + junos_vrf: + name: test-1 + state: absent + provider: "{{ netconf }}" + +- name: Configure vrf and its parameter + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/6 + - ge-0/0/5 + rd: 3.3.3.3:10 + target: target:65513:111 + state: present + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'+ test-1' in result.diff.prepared" + - "'+ description test-vrf-1;' in result.diff.prepared" + - "'+ instance-type vrf;' in result.diff.prepared" + - "'+ interface ge-0/0/5.0;' in result.diff.prepared" + - "'+ interface ge-0/0/6.0;' in result.diff.prepared" + - "'+ route-distinguisher 3.3.3.3:10;' in result.diff.prepared" + - "'+ vrf-target target:65513:111;' in result.diff.prepared" + +- name: Configure vrf and its parameter (idempotent) + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/6 + - ge-0/0/5 + rd: 3.3.3.3:10 + target: target:65513:111 + state: present + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == false" + +- name: Change vrf parameter + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'+ interface ge-0/0/2.0;' in result.diff.prepared" + - "'+ interface ge-0/0/3.0;' in result.diff.prepared" + - "'- interface ge-0/0/5.0;' in result.diff.prepared" + - "'- interface ge-0/0/6.0;' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'- route-distinguisher 3.3.3.3:10;' in result.diff.prepared" + - "'+ route-distinguisher 1.1.1.1:10;' in result.diff.prepared" + - "'- vrf-target target:65513:111;' in result.diff.prepared" + - "'+ vrf-target target:65514:113;' in result.diff.prepared" + + +- name: Deactivate vrf + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + active: False + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances]' in result.diff.prepared" + - "'! inactive: test-1' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! inactive: interface ge-0/0/2.0' in result.diff.prepared" + - "'! inactive: interface ge-0/0/3.0' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! inactive: route-distinguisher' in result.diff.prepared" + - "'! inactive: vrf-target' in result.diff.prepared" + +- name: Activate vrf + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + active: True + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances]' in result.diff.prepared" + - "'! active: test-1' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! active: interface ge-0/0/2.0' in result.diff.prepared" + - "'! active: interface ge-0/0/3.0' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! active: route-distinguisher' in result.diff.prepared" + - "'! active: vrf-target' in result.diff.prepared" + +- name: Delete vrf + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: absent + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances]' in result.diff.prepared" + - "'- test-1' in result.diff.prepared" + - "'- description test-vrf-1;' in result.diff.prepared" + - "'- instance-type vrf;' in result.diff.prepared" + - "'- interface ge-0/0/2.0;' in result.diff.prepared" + - "'- interface ge-0/0/3.0;' in result.diff.prepared" + - "'- route-distinguisher 1.1.1.1:10;' in result.diff.prepared" + - "'- vrf-target target:65514:113;' in result.diff.prepared" + +- name: Delete vrf (idempotent) + junos_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: absent + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == false" diff --git a/test/integration/targets/net_vrf/tasks/main.yaml b/test/integration/targets/net_vrf/tasks/main.yaml index 415c99d8b12..af08869c922 100644 --- a/test/integration/targets/net_vrf/tasks/main.yaml +++ b/test/integration/targets/net_vrf/tasks/main.yaml @@ -1,2 +1,3 @@ --- - { include: cli.yaml, tags: ['cli'] } +- { include: netconf.yaml, tags: ['netconf'] } diff --git a/test/integration/targets/net_vrf/tasks/netconf.yaml b/test/integration/targets/net_vrf/tasks/netconf.yaml new file mode 100644 index 00000000000..1286b354228 --- /dev/null +++ b/test/integration/targets/net_vrf/tasks/netconf.yaml @@ -0,0 +1,16 @@ +--- +- name: collect all netconf test cases + find: + paths: "{{ role_path }}/tests/netconf" + patterns: "{{ testcase }}.yaml" + register: test_cases + delegate_to: localhost + +- name: set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: run test case + include: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/net_vrf/tests/junos/basic.yaml b/test/integration/targets/net_vrf/tests/junos/basic.yaml new file mode 100644 index 00000000000..587a5702356 --- /dev/null +++ b/test/integration/targets/net_vrf/tests/junos/basic.yaml @@ -0,0 +1,171 @@ +--- +- debug: msg="START net_vrf junos/basic.yaml" + +- name: setup - remove vrf + net_vrf: + name: test-1 + state: absent + provider: "{{ netconf }}" + +- name: Configure vrf and its parameter + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/6 + - ge-0/0/5 + rd: 3.3.3.3:10 + target: target:65513:111 + state: present + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'+ test-1' in result.diff.prepared" + - "'+ description test-vrf-1;' in result.diff.prepared" + - "'+ instance-type vrf;' in result.diff.prepared" + - "'+ interface ge-0/0/5.0;' in result.diff.prepared" + - "'+ interface ge-0/0/6.0;' in result.diff.prepared" + - "'+ route-distinguisher 3.3.3.3:10;' in result.diff.prepared" + - "'+ vrf-target target:65513:111;' in result.diff.prepared" + +- name: Configure vrf and its parameter (idempotent) + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/6 + - ge-0/0/5 + rd: 3.3.3.3:10 + target: target:65513:111 + state: present + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == false" + +- name: Change vrf parameter + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'+ interface ge-0/0/2.0;' in result.diff.prepared" + - "'+ interface ge-0/0/3.0;' in result.diff.prepared" + - "'- interface ge-0/0/5.0;' in result.diff.prepared" + - "'- interface ge-0/0/6.0;' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'- route-distinguisher 3.3.3.3:10;' in result.diff.prepared" + - "'+ route-distinguisher 1.1.1.1:10;' in result.diff.prepared" + - "'- vrf-target target:65513:111;' in result.diff.prepared" + - "'+ vrf-target target:65514:113;' in result.diff.prepared" + + +- name: Deactivate vrf + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + active: False + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances]' in result.diff.prepared" + - "'! inactive: test-1' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! inactive: interface ge-0/0/2.0' in result.diff.prepared" + - "'! inactive: interface ge-0/0/3.0' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! inactive: route-distinguisher' in result.diff.prepared" + - "'! inactive: vrf-target' in result.diff.prepared" + +- name: Activate vrf + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: present + active: True + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances]' in result.diff.prepared" + - "'! active: test-1' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! active: interface ge-0/0/2.0' in result.diff.prepared" + - "'! active: interface ge-0/0/3.0' in result.diff.prepared" + - "'[edit routing-instances test-1]' in result.diff.prepared" + - "'! active: route-distinguisher' in result.diff.prepared" + - "'! active: vrf-target' in result.diff.prepared" + +- name: Delete vrf + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: absent + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == true" + - "'[edit routing-instances]' in result.diff.prepared" + - "'- test-1' in result.diff.prepared" + - "'- description test-vrf-1;' in result.diff.prepared" + - "'- instance-type vrf;' in result.diff.prepared" + - "'- interface ge-0/0/2.0;' in result.diff.prepared" + - "'- interface ge-0/0/3.0;' in result.diff.prepared" + - "'- route-distinguisher 1.1.1.1:10;' in result.diff.prepared" + - "'- vrf-target target:65514:113;' in result.diff.prepared" + +- name: Delete vrf (idempotent) + net_vrf: + name: test-1 + description: test-vrf-1 + interfaces: + - ge-0/0/3 + - ge-0/0/2 + rd: 1.1.1.1:10 + target: target:65514:113 + state: absent + provider: "{{ netconf }}" + register: result + +- assert: + that: + - "result.changed == false" diff --git a/test/integration/targets/net_vrf/tests/netconf/basic.yaml b/test/integration/targets/net_vrf/tests/netconf/basic.yaml new file mode 100644 index 00000000000..5ff7cf5af8e --- /dev/null +++ b/test/integration/targets/net_vrf/tests/netconf/basic.yaml @@ -0,0 +1,3 @@ +--- +- include: "{{ role_path }}/tests/junos/basic.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'junos'