summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGanesh Nalawade <ganesh634@gmail.com>2018-02-26 09:23:54 +0530
committerGitHub <noreply@github.com>2018-02-26 09:23:54 +0530
commit5a6b89324092cbdcc6a1e4c96925824c983c2823 (patch)
tree030c181efc884eb75047892064ba5dad0ad9aa6e
parent215c80f9dc44fe679c65862a811c706f1dfe0eb6 (diff)
downloadansible-5a6b89324092cbdcc6a1e4c96925824c983c2823.tar.gz
Decouple config and state check in vlan and vrf network modules (#36386)
* Decouple config and state check in {network_os }_vlan and { network_os }_vrf modules Fixes #35567 Fixes #34754 `interfaces` option is used for configuration as well as operational state check. If interface is configured to given vlan or vrf but if operational state of interface is disabled it results in module failure. Fix is to decouple same option usage for config and state. With this fix `interfaces` is used as config option and a new option named `associated_interfaces` will be used for intent check for assigned interfaces. * Fix CI failures * Fix review comment * Fixed integration test failure
-rw-r--r--lib/ansible/modules/network/eos/eos_vlan.py53
-rw-r--r--lib/ansible/modules/network/eos/eos_vrf.py49
-rw-r--r--lib/ansible/modules/network/ios/ios_vlan.py49
-rw-r--r--lib/ansible/modules/network/ios/ios_vrf.py26
-rw-r--r--lib/ansible/modules/network/nxos/nxos_vlan.py63
-rw-r--r--lib/ansible/modules/network/nxos/nxos_vrf.py52
-rw-r--r--lib/ansible/modules/network/vyos/vyos_vlan.py77
-rw-r--r--test/integration/targets/eos_vlan/tests/cli/basic.yaml21
-rw-r--r--test/integration/targets/eos_vrf/tests/cli/basic.yaml20
-rw-r--r--test/integration/targets/ios_vlan/tests/cli/basic.yaml25
-rw-r--r--test/integration/targets/nxos_vlan/tests/common/interface.yaml31
-rw-r--r--test/integration/targets/nxos_vrf/tests/common/intent.yaml32
-rw-r--r--test/integration/targets/vyos_vlan/tests/cli/intent.yaml59
13 files changed, 457 insertions, 100 deletions
diff --git a/lib/ansible/modules/network/eos/eos_vlan.py b/lib/ansible/modules/network/eos/eos_vlan.py
index 3ee50b160d..5958b9b0b0 100644
--- a/lib/ansible/modules/network/eos/eos_vlan.py
+++ b/lib/ansible/modules/network/eos/eos_vlan.py
@@ -47,6 +47,13 @@ options:
description:
- List of interfaces that should be associated to the VLAN. The name of interface
should be in expanded format and not abbreviated.
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vlan C(name)
+ for associated interfaces. The name of interface should be in expanded format and not abbreviated.
+ If the value in the C(associated_interfaces) does not match with the operational state of vlan
+ interfaces on device it will result in failure.
+ version_added: "2.5"
delay:
description:
- Delay the play should wait to check for declarative intent params values.
@@ -80,6 +87,13 @@ EXAMPLES = """
- Ethernet1
- Ethernet2
+- name: Check if interfaces is assigned to vlan
+ eos_vlan:
+ vlan_id: 4000
+ associated_interfaces:
+ - Ethernet1
+ - Ethernet2
+
- name: Suspend vlan
eos_vlan:
vlan_id: 4000
@@ -233,6 +247,9 @@ def map_params_to_obj(module):
if item.get('interfaces'):
item['interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('interfaces') if intf]
+ if item.get('associated_interfaces'):
+ item['associated_interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('associated_interfaces') if intf]
+
d = item.copy()
d['vlan_id'] = str(d['vlan_id'])
@@ -242,23 +259,35 @@ def map_params_to_obj(module):
'vlan_id': str(module.params['vlan_id']),
'name': module.params['name'],
'state': module.params['state'],
- 'interfaces': [intf.replace(" ", "").lower() for intf in module.params['interfaces']] if module.params['interfaces'] else []
+ 'interfaces': [intf.replace(" ", "").lower() for intf in module.params['interfaces']] if module.params['interfaces'] else [],
+ 'associated_interfaces': [intf.replace(" ", "").lower() for intf in
+ module.params['associated_interfaces']] if module.params['associated_interfaces'] else []
+
})
return obj
-def check_declarative_intent_params(want, module):
- if module.params['interfaces']:
- time.sleep(module.params['delay'])
- have = map_config_to_obj(module)
+def check_declarative_intent_params(want, module, result):
+ have = None
+ is_delay = False
+
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
+
+ if result['changed'] and not is_delay:
+ time.sleep(module.params['delay'])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(module)
- for w in want:
- for i in w['interfaces']:
- obj_in_have = search_obj_in_list(w['vlan_id'], have)
+ for i in w['associated_interfaces']:
+ obj_in_have = search_obj_in_list(w['vlan_id'], have)
- if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
- module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
+ if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
+ module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
def main():
@@ -268,6 +297,7 @@ def main():
vlan_id=dict(type='int'),
name=dict(),
interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
state=dict(default='present',
choices=['present', 'absent', 'active', 'suspend'])
@@ -316,8 +346,7 @@ def main():
result['session_name'] = response.get('session')
result['changed'] = True
- if result['changed']:
- check_declarative_intent_params(want, module)
+ check_declarative_intent_params(want, module, result)
module.exit_json(**result)
diff --git a/lib/ansible/modules/network/eos/eos_vrf.py b/lib/ansible/modules/network/eos/eos_vrf.py
index e26a9f12bc..c773355c3a 100644
--- a/lib/ansible/modules/network/eos/eos_vrf.py
+++ b/lib/ansible/modules/network/eos/eos_vrf.py
@@ -49,6 +49,12 @@ options:
should be configured in the VRF. Interfaces must be routed
interfaces in order to be placed into a VRF. The name of interface
should be in expanded format and not abbreviated.
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vrf C(name)
+ for associated interfaces. If the value in the C(associated_interfaces) does not match with
+ the operational state of vrf interfaces on device it will result in failure.
+ version_added: "2.5"
aggregate:
description: List of VRFs definitions
purge:
@@ -238,31 +244,46 @@ def map_params_to_obj(module):
if item.get('interfaces'):
item['interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('interfaces') if intf]
+ if item.get('associated_interfaces'):
+ item['associated_interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('associated_interfaces') if intf]
+
obj.append(item.copy())
else:
obj.append({
'name': module.params['name'],
'state': module.params['state'],
'rd': module.params['rd'],
- 'interfaces': [intf.replace(" ", "").lower() for intf in module.params['interfaces']] if module.params['interfaces'] else []
+ 'interfaces': [intf.replace(" ", "").lower() for intf in module.params['interfaces']] if module.params['interfaces'] else [],
+ 'associated_interfaces': [intf.replace(" ", "").lower() for intf in
+ module.params['associated_interfaces']] if module.params['associated_interfaces'] else []
+
})
return obj
-def check_declarative_intent_params(want, module):
- if module.params['interfaces']:
- time.sleep(module.params['delay'])
- have = map_config_to_obj(module)
+def check_declarative_intent_params(want, module, result):
+ have = None
+ is_delay = False
- for w in want:
- for i in w['interfaces']:
- obj_in_have = search_obj_in_list(w['name'], have)
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
- if obj_in_have:
- interfaces = obj_in_have.get('interfaces')
- if interfaces is not None and i not in interfaces:
- module.fail_json(msg="Interface %s not configured on vrf %s" % (i, w['name']))
+ if result['changed'] and not is_delay:
+ time.sleep(module.params['delay'])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(module)
+
+ for i in w['associated_interfaces']:
+ obj_in_have = search_obj_in_list(w['name'], have)
+
+ if obj_in_have:
+ interfaces = obj_in_have.get('interfaces')
+ if interfaces is not None and i not in interfaces:
+ module.fail_json(msg="Interface %s not configured on vrf %s" % (i, w['name']))
def main():
@@ -271,6 +292,7 @@ def main():
element_spec = dict(
name=dict(),
interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
rd=dict(),
state=dict(default='present', choices=['present', 'absent'])
@@ -318,8 +340,7 @@ def main():
result['session_name'] = response.get('session')
result['changed'] = True
- if result['changed']:
- check_declarative_intent_params(want, module)
+ check_declarative_intent_params(want, module, result)
module.exit_json(**result)
diff --git a/lib/ansible/modules/network/ios/ios_vlan.py b/lib/ansible/modules/network/ios/ios_vlan.py
index 9037120c93..cd3617e9bf 100644
--- a/lib/ansible/modules/network/ios/ios_vlan.py
+++ b/lib/ansible/modules/network/ios/ios_vlan.py
@@ -35,6 +35,12 @@ options:
description:
- List of interfaces that should be associated to the VLAN.
required: true
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vlan C(name)
+ for associated interfaces. If the value in the C(associated_interfaces) does not match with
+ the operational state of vlan interfaces on device it will result in failure.
+ version_added: "2.5"
delay:
description:
- Delay the play should wait to check for declarative intent params values.
@@ -59,12 +65,21 @@ EXAMPLES = """
vlan_id: 100
name: test-vlan
state: present
+
- name: Add interfaces to VLAN
ios_vlan:
vlan_id: 100
interfaces:
- GigabitEthernet0/0
- GigabitEthernet0/1
+
+- name: Check if interfaces is assigned to VLAN
+ ios_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - GigabitEthernet0/0
+ - GigabitEthernet0/1
+
- name: Delete vlan
ios_vlan:
vlan_id: 100
@@ -189,6 +204,7 @@ def map_params_to_obj(module):
'vlan_id': str(module.params['vlan_id']),
'name': module.params['name'],
'interfaces': module.params['interfaces'],
+ 'associated_interfaces': module.params['associated_interfaces'],
'state': module.params['state']
})
@@ -227,16 +243,26 @@ def map_config_to_obj(module):
return objs
-def check_declarative_intent_params(want, module):
- if module.params['interfaces']:
- time.sleep(module.params['delay'])
- have = map_config_to_obj(module)
+def check_declarative_intent_params(want, module, result):
+
+ have = None
+ is_delay = False
+
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
- for w in want:
- for i in w['interfaces']:
- obj_in_have = search_obj_in_list(w['vlan_id'], have)
- if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
- module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
+ if result['changed'] and not is_delay:
+ time.sleep(module.params['delay'])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(module)
+
+ for i in w['associated_interfaces']:
+ obj_in_have = search_obj_in_list(w['vlan_id'], have)
+ if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
+ module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
def main():
@@ -246,6 +272,7 @@ def main():
vlan_id=dict(type='int'),
name=dict(),
interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
state=dict(default='present',
choices=['present', 'absent', 'active', 'suspend'])
@@ -287,10 +314,10 @@ def main():
load_config(module, commands)
result['changed'] = True
- if result['changed']:
- check_declarative_intent_params(want, module)
+ check_declarative_intent_params(want, module, result)
module.exit_json(**result)
+
if __name__ == '__main__':
main()
diff --git a/lib/ansible/modules/network/ios/ios_vrf.py b/lib/ansible/modules/network/ios/ios_vrf.py
index de3e58e9d8..70fee2f745 100644
--- a/lib/ansible/modules/network/ios/ios_vrf.py
+++ b/lib/ansible/modules/network/ios/ios_vrf.py
@@ -64,6 +64,12 @@ options:
- 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.
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vrf C(name)
+ for associated interfaces. If the value in the C(associated_interfaces) does not match with
+ the operational state of vrf interfaces on device it will result in failure.
+ version_added: "2.5"
delay:
description:
- Time in seconds to wait before checking for the operational state on remote
@@ -400,6 +406,7 @@ def map_params_to_obj(module):
item['route_import'] = get_value('route_import')
item['route_export'] = get_value('route_export')
item['route_both'] = get_value('route_both')
+ item['associated_interfaces'] = get_value('associated_interfaces')
objects.append(item)
return objects
@@ -424,8 +431,12 @@ def update_objects(want, have):
return updates
-def check_declarative_intent_params(want, module):
- if module.params['interfaces']:
+def check_declarative_intent_params(want, module, result):
+ if module.params['associated_interfaces']:
+
+ if result['changed']:
+ time.sleep(module.params['delay'])
+
name = module.params['name']
rc, out, err = exec_command(module, 'show vrf | include {0}'.format(name))
@@ -439,7 +450,9 @@ def check_declarative_intent_params(want, module):
for w in want:
if w['name'] == vrf:
- for i in w['interfaces']:
+ if w.get('associated_interfaces') is None:
+ continue
+ for i in w['associated_interfaces']:
if get_interface_type(i) is not get_interface_type(interface):
module.fail_json(msg="Interface %s not configured on vrf %s" % (interface, name))
@@ -458,6 +471,7 @@ def main():
route_both=dict(type='list'),
interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
purge=dict(type='bool', default=False),
@@ -497,12 +511,10 @@ def main():
load_config(module, commands)
result['changed'] = True
- if result['changed']:
- time.sleep(module.params['delay'])
-
- check_declarative_intent_params(want, module)
+ check_declarative_intent_params(want, module, result)
module.exit_json(**result)
+
if __name__ == '__main__':
main()
diff --git a/lib/ansible/modules/network/nxos/nxos_vlan.py b/lib/ansible/modules/network/nxos/nxos_vlan.py
index cc668dbdbb..cb5d339b67 100644
--- a/lib/ansible/modules/network/nxos/nxos_vlan.py
+++ b/lib/ansible/modules/network/nxos/nxos_vlan.py
@@ -49,6 +49,12 @@ options:
description:
- List of interfaces that should be associated to the VLAN.
version_added: "2.5"
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vlan C(name)
+ for associated interfaces. If the value in the C(associated_interfaces) does not match with
+ the operational state of vlan interfaces on device it will result in failure.
+ version_added: "2.5"
vlan_state:
description:
- Manage the vlan operational state of the VLAN
@@ -116,12 +122,22 @@ EXAMPLES = '''
vlan_id: 50
state: absent
-- name: Add interfaces to VLAN
+- name: Add interfaces to VLAN and check intent (config + intent)
nxos_vlan:
vlan_id: 100
interfaces:
- Ethernet2/1
- Ethernet2/5
+ associated_interfaces:
+ - Ethernet2/1
+ - Ethernet2/5
+
+- name: Check interfaces assigned to VLAN
+ nxos_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - Ethernet2/1
+ - Ethernet2/5
- name: Create aggregate of vlans
nxos_vlan:
@@ -156,13 +172,15 @@ def search_obj_in_list(vlan_id, lst):
def get_diff(w, have):
- del w['interfaces']
- del w['name']
+ c = deepcopy(w)
+ del c['interfaces']
+ del c['name']
+ del c['associated_interfaces']
for o in have:
del o['interfaces']
del o['name']
if o['vlan_id'] == w['vlan_id']:
- diff_dict = dict(set(w.items()) - set(o.items()))
+ diff_dict = dict(set(c.items()) - set(o.items()))
return diff_dict
@@ -339,7 +357,8 @@ def map_params_to_obj(module):
'mapped_vni': str(module.params['mapped_vni']),
'state': module.params['state'],
'admin_state': module.params['admin_state'],
- 'mode': module.params['mode']
+ 'mode': module.params['mode'],
+ 'associated_interfaces': module.params['associated_interfaces']
})
return obj
@@ -451,16 +470,26 @@ def map_config_to_obj(module, os_platform):
return objs
-def check_declarative_intent_params(want, module, os_platform):
- if module.params['interfaces']:
- time.sleep(module.params['delay'])
- have = map_config_to_obj(module, os_platform)
+def check_declarative_intent_params(want, module, os_platform, result):
+
+ have = None
+ is_delay = False
+
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
- for w in want:
- for i in w['interfaces']:
- obj_in_have = search_obj_in_list(w['vlan_id'], have)
- if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
- module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
+ if result['changed'] and not is_delay:
+ time.sleep(module.params['delay'])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(module, os_platform)
+
+ for i in w['associated_interfaces']:
+ obj_in_have = search_obj_in_list(w['vlan_id'], have)
+ if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
+ module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
def main():
@@ -471,6 +500,7 @@ def main():
vlan_range=dict(required=False),
name=dict(required=False),
interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
vlan_state=dict(choices=['active', 'suspend'], required=False, default='active'),
mapped_vni=dict(required=False, type='int'),
delay=dict(default=10, type='int'),
@@ -526,10 +556,11 @@ def main():
load_config(module, commands)
result['changed'] = True
- if want and result['changed']:
- check_declarative_intent_params(want, module, os_platform)
+ if want:
+ check_declarative_intent_params(want, module, os_platform, result)
module.exit_json(**result)
+
if __name__ == '__main__':
main()
diff --git a/lib/ansible/modules/network/nxos/nxos_vrf.py b/lib/ansible/modules/network/nxos/nxos_vrf.py
index 59a458e2e6..35388606fe 100644
--- a/lib/ansible/modules/network/nxos/nxos_vrf.py
+++ b/lib/ansible/modules/network/nxos/nxos_vrf.py
@@ -76,6 +76,12 @@ options:
- List of interfaces to check the VRF has been
configured correctly.
version_added: 2.5
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vrf C(name)
+ for associated interfaces. If the value in the C(associated_interfaces) does not match with
+ the operational state of vrf interfaces on device it will result in failure.
+ version_added: "2.5"
aggregate:
description: List of VRFs definitions.
version_added: 2.5
@@ -137,6 +143,13 @@ EXAMPLES = '''
- Ethernet2/3
- Ethernet2/5
+- name: Check interfaces assigend to VRF
+ nxos_vrf:
+ name: test1
+ associated_interfaces:
+ - Ethernet2/3
+ - Ethernet2/5
+
- name: Ensure VRF is tagged with interface Ethernet2/5 only (Removes from Ethernet2/3)
nxos_vrf:
name: test1
@@ -349,7 +362,8 @@ def map_params_to_obj(module):
'rd': module.params['rd'],
'admin_state': module.params['admin_state'],
'state': module.params['state'],
- 'interfaces': module.params['interfaces']
+ 'interfaces': module.params['interfaces'],
+ 'associated_interfaces': module.params['associated_interfaces']
})
return obj
@@ -408,19 +422,29 @@ def map_config_to_obj(want, element_spec, module):
return objs
-def check_declarative_intent_params(want, element_spec, module):
- if module.params['interfaces']:
- time.sleep(module.params['delay'])
- have = map_config_to_obj(want, element_spec, module)
+def check_declarative_intent_params(want, module, element_spec, result):
+
+ have = None
+ is_delay = False
+
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
+
+ if result['changed'] and not is_delay:
+ time.sleep(module.params['delay'])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(want, element_spec, module)
- for w in want:
- for i in w['interfaces']:
- obj_in_have = search_obj_in_list(w['name'], have)
+ for i in w['associated_interfaces']:
+ obj_in_have = search_obj_in_list(w['name'], have)
- if obj_in_have:
- interfaces = obj_in_have.get('interfaces')
- if interfaces is not None and i not in interfaces:
- module.fail_json(msg="Interface %s not configured on vrf %s" % (i, w['name']))
+ if obj_in_have:
+ interfaces = obj_in_have.get('interfaces')
+ if interfaces is not None and i not in interfaces:
+ module.fail_json(msg="Interface %s not configured on vrf %s" % (i, w['name']))
def main():
@@ -433,6 +457,7 @@ def main():
rd=dict(type=str),
admin_state=dict(default='up', choices=['up', 'down']),
interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
state=dict(default='present', choices=['present', 'absent'])
)
@@ -472,8 +497,7 @@ def main():
load_config(module, commands)
result['changed'] = True
- if result['changed']:
- check_declarative_intent_params(want, element_spec, module)
+ check_declarative_intent_params(want, module, element_spec, result)
module.exit_json(**result)
diff --git a/lib/ansible/modules/network/vyos/vyos_vlan.py b/lib/ansible/modules/network/vyos/vyos_vlan.py
index e298d5dbf9..d2613ed663 100644
--- a/lib/ansible/modules/network/vyos/vyos_vlan.py
+++ b/lib/ansible/modules/network/vyos/vyos_vlan.py
@@ -38,6 +38,12 @@ options:
description:
- List of interfaces that should be associated to the VLAN.
required: true
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vlan C(name)
+ for associated interfaces. If the value in the C(associated_interfaces) does not match with
+ the operational state of vlan on device it will result in failure.
+ version_added: "2.5"
delay:
description:
- Delay the play should wait to check for declarative intent params values.
@@ -77,6 +83,20 @@ EXAMPLES = """
interfaces: eth1
address: 172.26.100.37/24
+- name: vlan interface config + intent
+ vyos_vlan:
+ vlan_id: 100
+ interfaces: eth0
+ associated_interfaces:
+ - eth0
+
+- name: vlan intent check
+ vyos_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - eth3
+ - eth4
+
- name: Delete vlan
vyos_vlan:
vlan_id: 100
@@ -166,6 +186,7 @@ def map_params_to_obj(module):
d = item.copy()
d['vlan_id'] = str(d['vlan_id'])
+ module._check_required_one_of(module.required_one_of, item)
obj.append(d)
else:
@@ -174,7 +195,8 @@ def map_params_to_obj(module):
'name': module.params['name'],
'address': module.params['address'],
'state': module.params['state'],
- 'interfaces': module.params['interfaces']
+ 'interfaces': module.params['interfaces'],
+ 'associated_interfaces': module.params['associated_interfaces']
})
return obj
@@ -213,26 +235,34 @@ def map_config_to_obj(module):
return objs
-def check_declarative_intent_params(want, module):
- if module.params['interfaces']:
- time.sleep(module.params['delay'])
- have = map_config_to_obj(module)
+def check_declarative_intent_params(want, module, result):
- want_interface = list()
- obj_interface = list()
+ have = None
+ obj_interface = list()
+ is_delay = False
- for w in want:
- for i in w['interfaces']:
- want_interface.append(i)
- obj_in_have = search_obj_in_list(w['vlan_id'], have)
- if obj_in_have:
- for obj in obj_in_have:
- obj_interface.extend(obj['interfaces'])
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
+
+ if result['changed'] and not is_delay:
+ time.sleep(module.params['delay'])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(module)
- for w in want:
- for i in w['interfaces']:
- if (set(obj_interface) - set(want_interface)) != set([]):
- module.fail_json(msg='Interface {0} not configured on vlan {1}'.format(i, w['vlan_id']))
+ obj_in_have = search_obj_in_list(w['vlan_id'], have)
+ if obj_in_have:
+ for obj in obj_in_have:
+ obj_interface.extend(obj['interfaces'])
+
+ for w in want:
+ if w.get('associated_interfaces') is None:
+ continue
+ for i in w['associated_interfaces']:
+ if (set(obj_interface) - set(w['associated_interfaces'])) != set([]):
+ module.fail_json(msg='Interface {0} not configured on vlan {1}'.format(i, w['vlan_id']))
def main():
@@ -242,7 +272,8 @@ def main():
vlan_id=dict(type='int', required=True),
name=dict(),
address=dict(),
- interfaces=dict(type='list', required=True),
+ interfaces=dict(type='list'),
+ associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
state=dict(default='present',
choices=['present', 'absent'])
@@ -261,7 +292,9 @@ def main():
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
- required_one_of = [['vlan_id', 'aggregate']]
+ required_one_of = [['vlan_id', 'aggregate'],
+ ['interfaces', 'associated_interfaces']]
+
mutually_exclusive = [['vlan_id', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
@@ -285,10 +318,10 @@ def main():
load_config(module, commands, commit=commit)
result['changed'] = True
- if result['changed']:
- check_declarative_intent_params(want, module)
+ check_declarative_intent_params(want, module, result)
module.exit_json(**result)
+
if __name__ == '__main__':
main()
diff --git a/test/integration/targets/eos_vlan/tests/cli/basic.yaml b/test/integration/targets/eos_vlan/tests/cli/basic.yaml
index 7da49d6080..b3821230e6 100644
--- a/test/integration/targets/eos_vlan/tests/cli/basic.yaml
+++ b/test/integration/targets/eos_vlan/tests/cli/basic.yaml
@@ -118,13 +118,16 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033
- "'ansible_1' in result.session_name"
-- name: Add interfaces to vlan
+- name: Add interfaces to vlan and check state
eos_vlan:
vlan_id: 4000
state: present
interfaces:
- Ethernet1
- Ethernet2
+ associated_interfaces:
+ - Ethernet1
+ - Ethernet2
authorize: yes
provider: "{{ cli }}"
become: yes
@@ -160,6 +163,22 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033
- "result.session_name is not defined"
+- name: vlan interface intent fail
+ eos_vlan:
+ vlan_id: 4000
+ state: present
+ associated_interfaces:
+ - test
+ authorize: yes
+ provider: "{{ cli }}"
+ become: yes
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == True"
+
- name: Remove interface from vlan
eos_vlan:
vlan_id: 4000
diff --git a/test/integration/targets/eos_vrf/tests/cli/basic.yaml b/test/integration/targets/eos_vrf/tests/cli/basic.yaml
index 2413864c4a..efcf721007 100644
--- a/test/integration/targets/eos_vrf/tests/cli/basic.yaml
+++ b/test/integration/targets/eos_vrf/tests/cli/basic.yaml
@@ -85,7 +85,7 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033
- "result.session_name is not defined"
-- name: Add Ethernet2 to vrf
+- name: Add Ethernet2 to vrf and check interface assigned state
eos_vrf:
name: test
rd: 1:201
@@ -93,6 +93,8 @@
authorize: yes
interfaces:
- Ethernet2
+ associated_interfaces:
+ - Ethernet2
provider: "{{ cli }}"
become: yes
register: result
@@ -124,6 +126,22 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033
- "'session_name' not in result.commands"
+- name: vrf interface intent fail
+ eos_vrf:
+ name: test
+ state: present
+ authorize: yes
+ associated_interfaces:
+ - test
+ provider: "{{ cli }}"
+ become: yes
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == True"
+
- name: Add multiple interfaces to vrf
eos_vrf:
name: test1
diff --git a/test/integration/targets/ios_vlan/tests/cli/basic.yaml b/test/integration/targets/ios_vlan/tests/cli/basic.yaml
index 599a869114..1634b091fe 100644
--- a/test/integration/targets/ios_vlan/tests/cli/basic.yaml
+++ b/test/integration/targets/ios_vlan/tests/cli/basic.yaml
@@ -72,6 +72,31 @@
that:
- "result.changed == false"
+ - name: Check interface assigned to vrf (intent)
+ ios_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - GigabitEthernet0/1
+ - GigabitEthernet0/2
+ provider: "{{ cli }}"
+ register: result
+
+ - assert:
+ that:
+ - "result.failed == false"
+
+ - name: Check interface assigned to vrf (fail)
+ ios_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - test
+ provider: "{{ cli }}"
+ register: result
+
+ - assert:
+ that:
+ - "result.failed == True"
+
- name: Remove interface from vlan
ios_vlan: &single_int
vlan_id: 100
diff --git a/test/integration/targets/nxos_vlan/tests/common/interface.yaml b/test/integration/targets/nxos_vlan/tests/common/interface.yaml
index 7adc108b8a..cba7372ce5 100644
--- a/test/integration/targets/nxos_vlan/tests/common/interface.yaml
+++ b/test/integration/targets/nxos_vlan/tests/common/interface.yaml
@@ -29,12 +29,15 @@
vlan_id: 100
provider: "{{ connection }}"
-- name: Add interfaces to vlan
+- name: Add interfaces to vlan and check intent (config + intent)
nxos_vlan: &interfaces
vlan_id: 100
interfaces:
- "{{ testint1 }}"
- "{{ testint2 }}"
+ associated_interfaces:
+ - "{{ testint1 }}"
+ - "{{ testint2 }}"
provider: "{{ connection }}"
register: result
@@ -58,6 +61,32 @@
that:
- 'result.changed == false'
+- name: Check interfaces intent
+ nxos_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - "{{ testint1 }}"
+ - "{{ testint2 }}"
+ provider: "{{ connection }}"
+ register: result
+
+- assert:
+ that:
+ - "result.failed == false"
+
+- name: Check interfaces intent fail
+ nxos_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - test
+ provider: "{{ connection }}"
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == True"
+
- name: Remove interface from vlan
nxos_vlan: &single_int
vlan_id: 100
diff --git a/test/integration/targets/nxos_vrf/tests/common/intent.yaml b/test/integration/targets/nxos_vrf/tests/common/intent.yaml
index 25da09f865..5fa5a00581 100644
--- a/test/integration/targets/nxos_vrf/tests/common/intent.yaml
+++ b/test/integration/targets/nxos_vrf/tests/common/intent.yaml
@@ -64,18 +64,22 @@
that:
- "result.changed == false"
-- name: Assign interfaces to VRF
+- name: Assign interfaces to VRF (Config + intent)
nxos_vrf: &interfaces
name: test1
interfaces:
- "{{ testint1 }}"
- "{{ testint2 }}"
+ associated_interfaces:
+ - "{{ testint1 }}"
+ - "{{ testint2 }}"
provider: "{{ connection }}"
register: result
- assert:
that:
- 'result.changed == true'
+ - "result.failed == false"
- '"interface {{ testint1 }}" in result.commands'
- '"vrf member test1" in result.commands'
- '"interface {{ testint2 }}" in result.commands'
@@ -89,6 +93,32 @@
that:
- 'result.changed == false'
+- name: Check interfaces assigned to VRF (intent)
+ nxos_vrf:
+ name: test1
+ associated_interfaces:
+ - "{{ testint1 }}"
+ - "{{ testint2 }}"
+ provider: "{{ connection }}"
+ register: result
+
+- assert:
+ that:
+ - "result.failed == false"
+
+- name: Assign interfaces to VRF (intent fail)
+ nxos_vrf:
+ name: test1
+ associated_interfaces:
+ - test
+ provider: "{{ connection }}"
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == True"
+
- name: Remove interface from vrf
nxos_vrf: &single_int
name: test1
diff --git a/test/integration/targets/vyos_vlan/tests/cli/intent.yaml b/test/integration/targets/vyos_vlan/tests/cli/intent.yaml
new file mode 100644
index 0000000000..98c8697d18
--- /dev/null
+++ b/test/integration/targets/vyos_vlan/tests/cli/intent.yaml
@@ -0,0 +1,59 @@
+---
+- debug: msg="START cli/intent.yaml on connection={{ ansible_connection }}"
+
+- name: setup - remove vlan used in test
+ vyos_config: &delete
+ lines:
+ - delete interfaces ethernet eth1 vif 100
+ - delete interfaces ethernet eth0 vif 100
+
+- name: set vlan with name
+ vyos_vlan:
+ vlan_id: 100
+ name: vlan-100
+ interfaces: eth1
+ register: result
+
+- assert:
+ that:
+ - "result.changed == true"
+ - "'set interfaces ethernet eth1 vif 100 description vlan-100' in result.commands"
+
+- name: check vlan interface intent
+ vyos_vlan:
+ vlan_id: 100
+ name: vlan-100
+ associated_interfaces: eth1
+ register: result
+
+- assert:
+ that:
+ - "result.failed == false"
+
+- name: vlan interface config + intent
+ vyos_vlan:
+ vlan_id: 100
+ interfaces: eth0
+ associated_interfaces:
+ - eth0
+ - eth1
+ register: result
+
+- assert:
+ that:
+ - "result.failed == false"
+
+- name: vlan intent fail
+ vyos_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - eth3
+ - eth4
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == True"
+
+- debug: msg="End cli/intent.yaml on connection={{ ansible_connection }}"