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
pull/36706/head
Ganesh Nalawade 7 years ago committed by GitHub
parent 215c80f9dc
commit 5a6b893240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -47,6 +47,13 @@ options:
description: description:
- List of interfaces that should be associated to the VLAN. The name of interface - List of interfaces that should be associated to the VLAN. The name of interface
should be in expanded format and not abbreviated. 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: delay:
description: description:
- Delay the play should wait to check for declarative intent params values. - Delay the play should wait to check for declarative intent params values.
@ -80,6 +87,13 @@ EXAMPLES = """
- Ethernet1 - Ethernet1
- Ethernet2 - Ethernet2
- name: Check if interfaces is assigned to vlan
eos_vlan:
vlan_id: 4000
associated_interfaces:
- Ethernet1
- Ethernet2
- name: Suspend vlan - name: Suspend vlan
eos_vlan: eos_vlan:
vlan_id: 4000 vlan_id: 4000
@ -233,6 +247,9 @@ def map_params_to_obj(module):
if item.get('interfaces'): if item.get('interfaces'):
item['interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('interfaces') if intf] 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 = item.copy()
d['vlan_id'] = str(d['vlan_id']) d['vlan_id'] = str(d['vlan_id'])
@ -242,19 +259,31 @@ def map_params_to_obj(module):
'vlan_id': str(module.params['vlan_id']), 'vlan_id': str(module.params['vlan_id']),
'name': module.params['name'], 'name': module.params['name'],
'state': module.params['state'], '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 return obj
def check_declarative_intent_params(want, module): def check_declarative_intent_params(want, module, result):
if module.params['interfaces']: 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']) time.sleep(module.params['delay'])
is_delay = True
if have is None:
have = map_config_to_obj(module) have = map_config_to_obj(module)
for w in want: for i in w['associated_interfaces']:
for i in w['interfaces']:
obj_in_have = search_obj_in_list(w['vlan_id'], have) 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']: if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
@ -268,6 +297,7 @@ def main():
vlan_id=dict(type='int'), vlan_id=dict(type='int'),
name=dict(), name=dict(),
interfaces=dict(type='list'), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
state=dict(default='present', state=dict(default='present',
choices=['present', 'absent', 'active', 'suspend']) choices=['present', 'absent', 'active', 'suspend'])
@ -316,8 +346,7 @@ def main():
result['session_name'] = response.get('session') result['session_name'] = response.get('session')
result['changed'] = True result['changed'] = True
if result['changed']: check_declarative_intent_params(want, module, result)
check_declarative_intent_params(want, module)
module.exit_json(**result) module.exit_json(**result)

@ -49,6 +49,12 @@ options:
should be configured in the VRF. Interfaces must be routed should be configured in the VRF. Interfaces must be routed
interfaces in order to be placed into a VRF. The name of interface interfaces in order to be placed into a VRF. The name of interface
should be in expanded format and not abbreviated. 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: aggregate:
description: List of VRFs definitions description: List of VRFs definitions
purge: purge:
@ -238,25 +244,40 @@ def map_params_to_obj(module):
if item.get('interfaces'): if item.get('interfaces'):
item['interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('interfaces') if intf] 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()) obj.append(item.copy())
else: else:
obj.append({ obj.append({
'name': module.params['name'], 'name': module.params['name'],
'state': module.params['state'], 'state': module.params['state'],
'rd': module.params['rd'], '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 return obj
def check_declarative_intent_params(want, module): def check_declarative_intent_params(want, module, result):
if module.params['interfaces']: 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']) time.sleep(module.params['delay'])
is_delay = True
if have is None:
have = map_config_to_obj(module) have = map_config_to_obj(module)
for w in want: for i in w['associated_interfaces']:
for i in w['interfaces']:
obj_in_have = search_obj_in_list(w['name'], have) obj_in_have = search_obj_in_list(w['name'], have)
if obj_in_have: if obj_in_have:
@ -271,6 +292,7 @@ def main():
element_spec = dict( element_spec = dict(
name=dict(), name=dict(),
interfaces=dict(type='list'), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
rd=dict(), rd=dict(),
state=dict(default='present', choices=['present', 'absent']) state=dict(default='present', choices=['present', 'absent'])
@ -318,8 +340,7 @@ def main():
result['session_name'] = response.get('session') result['session_name'] = response.get('session')
result['changed'] = True result['changed'] = True
if result['changed']: check_declarative_intent_params(want, module, result)
check_declarative_intent_params(want, module)
module.exit_json(**result) module.exit_json(**result)

@ -35,6 +35,12 @@ options:
description: description:
- List of interfaces that should be associated to the VLAN. - List of interfaces that should be associated to the VLAN.
required: true 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: delay:
description: description:
- Delay the play should wait to check for declarative intent params values. - Delay the play should wait to check for declarative intent params values.
@ -59,12 +65,21 @@ EXAMPLES = """
vlan_id: 100 vlan_id: 100
name: test-vlan name: test-vlan
state: present state: present
- name: Add interfaces to VLAN - name: Add interfaces to VLAN
ios_vlan: ios_vlan:
vlan_id: 100 vlan_id: 100
interfaces: interfaces:
- GigabitEthernet0/0 - GigabitEthernet0/0
- GigabitEthernet0/1 - GigabitEthernet0/1
- name: Check if interfaces is assigned to VLAN
ios_vlan:
vlan_id: 100
associated_interfaces:
- GigabitEthernet0/0
- GigabitEthernet0/1
- name: Delete vlan - name: Delete vlan
ios_vlan: ios_vlan:
vlan_id: 100 vlan_id: 100
@ -189,6 +204,7 @@ def map_params_to_obj(module):
'vlan_id': str(module.params['vlan_id']), 'vlan_id': str(module.params['vlan_id']),
'name': module.params['name'], 'name': module.params['name'],
'interfaces': module.params['interfaces'], 'interfaces': module.params['interfaces'],
'associated_interfaces': module.params['associated_interfaces'],
'state': module.params['state'] 'state': module.params['state']
}) })
@ -227,13 +243,23 @@ def map_config_to_obj(module):
return objs return objs
def check_declarative_intent_params(want, module): def check_declarative_intent_params(want, module, result):
if module.params['interfaces']:
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']) time.sleep(module.params['delay'])
is_delay = True
if have is None:
have = map_config_to_obj(module) have = map_config_to_obj(module)
for w in want: for i in w['associated_interfaces']:
for i in w['interfaces']:
obj_in_have = search_obj_in_list(w['vlan_id'], have) 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']: 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'])) module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
@ -246,6 +272,7 @@ def main():
vlan_id=dict(type='int'), vlan_id=dict(type='int'),
name=dict(), name=dict(),
interfaces=dict(type='list'), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
state=dict(default='present', state=dict(default='present',
choices=['present', 'absent', 'active', 'suspend']) choices=['present', 'absent', 'active', 'suspend'])
@ -287,10 +314,10 @@ def main():
load_config(module, commands) load_config(module, commands)
result['changed'] = True result['changed'] = True
if result['changed']: check_declarative_intent_params(want, module, result)
check_declarative_intent_params(want, module)
module.exit_json(**result) module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -64,6 +64,12 @@ options:
- Identifies the set of interfaces that - Identifies the set of interfaces that
should be configured in the VRF. Interfaces must be routed should be configured in the VRF. Interfaces must be routed
interfaces in order to be placed into a VRF. 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: delay:
description: description:
- Time in seconds to wait before checking for the operational state on remote - 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_import'] = get_value('route_import')
item['route_export'] = get_value('route_export') item['route_export'] = get_value('route_export')
item['route_both'] = get_value('route_both') item['route_both'] = get_value('route_both')
item['associated_interfaces'] = get_value('associated_interfaces')
objects.append(item) objects.append(item)
return objects return objects
@ -424,8 +431,12 @@ def update_objects(want, have):
return updates return updates
def check_declarative_intent_params(want, module): def check_declarative_intent_params(want, module, result):
if module.params['interfaces']: if module.params['associated_interfaces']:
if result['changed']:
time.sleep(module.params['delay'])
name = module.params['name'] name = module.params['name']
rc, out, err = exec_command(module, 'show vrf | include {0}'.format(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: for w in want:
if w['name'] == vrf: 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): if get_interface_type(i) is not get_interface_type(interface):
module.fail_json(msg="Interface %s not configured on vrf %s" % (interface, name)) module.fail_json(msg="Interface %s not configured on vrf %s" % (interface, name))
@ -458,6 +471,7 @@ def main():
route_both=dict(type='list'), route_both=dict(type='list'),
interfaces=dict(type='list'), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
purge=dict(type='bool', default=False), purge=dict(type='bool', default=False),
@ -497,12 +511,10 @@ def main():
load_config(module, commands) load_config(module, commands)
result['changed'] = True result['changed'] = True
if result['changed']: check_declarative_intent_params(want, module, result)
time.sleep(module.params['delay'])
check_declarative_intent_params(want, module)
module.exit_json(**result) module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -49,6 +49,12 @@ options:
description: description:
- List of interfaces that should be associated to the VLAN. - List of interfaces that should be associated to the VLAN.
version_added: "2.5" 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: vlan_state:
description: description:
- Manage the vlan operational state of the VLAN - Manage the vlan operational state of the VLAN
@ -116,12 +122,22 @@ EXAMPLES = '''
vlan_id: 50 vlan_id: 50
state: absent state: absent
- name: Add interfaces to VLAN - name: Add interfaces to VLAN and check intent (config + intent)
nxos_vlan: nxos_vlan:
vlan_id: 100 vlan_id: 100
interfaces: interfaces:
- Ethernet2/1 - Ethernet2/1
- Ethernet2/5 - 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 - name: Create aggregate of vlans
nxos_vlan: nxos_vlan:
@ -156,13 +172,15 @@ def search_obj_in_list(vlan_id, lst):
def get_diff(w, have): def get_diff(w, have):
del w['interfaces'] c = deepcopy(w)
del w['name'] del c['interfaces']
del c['name']
del c['associated_interfaces']
for o in have: for o in have:
del o['interfaces'] del o['interfaces']
del o['name'] del o['name']
if o['vlan_id'] == w['vlan_id']: 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 return diff_dict
@ -339,7 +357,8 @@ def map_params_to_obj(module):
'mapped_vni': str(module.params['mapped_vni']), 'mapped_vni': str(module.params['mapped_vni']),
'state': module.params['state'], 'state': module.params['state'],
'admin_state': module.params['admin_state'], 'admin_state': module.params['admin_state'],
'mode': module.params['mode'] 'mode': module.params['mode'],
'associated_interfaces': module.params['associated_interfaces']
}) })
return obj return obj
@ -451,13 +470,23 @@ def map_config_to_obj(module, os_platform):
return objs return objs
def check_declarative_intent_params(want, module, os_platform): def check_declarative_intent_params(want, module, os_platform, result):
if module.params['interfaces']:
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']) time.sleep(module.params['delay'])
is_delay = True
if have is None:
have = map_config_to_obj(module, os_platform) have = map_config_to_obj(module, os_platform)
for w in want: for i in w['associated_interfaces']:
for i in w['interfaces']:
obj_in_have = search_obj_in_list(w['vlan_id'], have) 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']: 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'])) module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
@ -471,6 +500,7 @@ def main():
vlan_range=dict(required=False), vlan_range=dict(required=False),
name=dict(required=False), name=dict(required=False),
interfaces=dict(type='list'), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
vlan_state=dict(choices=['active', 'suspend'], required=False, default='active'), vlan_state=dict(choices=['active', 'suspend'], required=False, default='active'),
mapped_vni=dict(required=False, type='int'), mapped_vni=dict(required=False, type='int'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
@ -526,10 +556,11 @@ def main():
load_config(module, commands) load_config(module, commands)
result['changed'] = True result['changed'] = True
if want and result['changed']: if want:
check_declarative_intent_params(want, module, os_platform) check_declarative_intent_params(want, module, os_platform, result)
module.exit_json(**result) module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -76,6 +76,12 @@ options:
- List of interfaces to check the VRF has been - List of interfaces to check the VRF has been
configured correctly. configured correctly.
version_added: 2.5 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: aggregate:
description: List of VRFs definitions. description: List of VRFs definitions.
version_added: 2.5 version_added: 2.5
@ -137,6 +143,13 @@ EXAMPLES = '''
- Ethernet2/3 - Ethernet2/3
- Ethernet2/5 - 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) - name: Ensure VRF is tagged with interface Ethernet2/5 only (Removes from Ethernet2/3)
nxos_vrf: nxos_vrf:
name: test1 name: test1
@ -349,7 +362,8 @@ def map_params_to_obj(module):
'rd': module.params['rd'], 'rd': module.params['rd'],
'admin_state': module.params['admin_state'], 'admin_state': module.params['admin_state'],
'state': module.params['state'], 'state': module.params['state'],
'interfaces': module.params['interfaces'] 'interfaces': module.params['interfaces'],
'associated_interfaces': module.params['associated_interfaces']
}) })
return obj return obj
@ -408,13 +422,23 @@ def map_config_to_obj(want, element_spec, module):
return objs return objs
def check_declarative_intent_params(want, element_spec, module): def check_declarative_intent_params(want, module, element_spec, result):
if module.params['interfaces']:
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']) time.sleep(module.params['delay'])
is_delay = True
if have is None:
have = map_config_to_obj(want, element_spec, module) have = map_config_to_obj(want, element_spec, module)
for w in want: for i in w['associated_interfaces']:
for i in w['interfaces']:
obj_in_have = search_obj_in_list(w['name'], have) obj_in_have = search_obj_in_list(w['name'], have)
if obj_in_have: if obj_in_have:
@ -433,6 +457,7 @@ def main():
rd=dict(type=str), rd=dict(type=str),
admin_state=dict(default='up', choices=['up', 'down']), admin_state=dict(default='up', choices=['up', 'down']),
interfaces=dict(type='list'), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
state=dict(default='present', choices=['present', 'absent']) state=dict(default='present', choices=['present', 'absent'])
) )
@ -472,8 +497,7 @@ def main():
load_config(module, commands) load_config(module, commands)
result['changed'] = True result['changed'] = True
if result['changed']: check_declarative_intent_params(want, module, element_spec, result)
check_declarative_intent_params(want, element_spec, module)
module.exit_json(**result) module.exit_json(**result)

@ -38,6 +38,12 @@ options:
description: description:
- List of interfaces that should be associated to the VLAN. - List of interfaces that should be associated to the VLAN.
required: true 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: delay:
description: description:
- Delay the play should wait to check for declarative intent params values. - Delay the play should wait to check for declarative intent params values.
@ -77,6 +83,20 @@ EXAMPLES = """
interfaces: eth1 interfaces: eth1
address: 172.26.100.37/24 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 - name: Delete vlan
vyos_vlan: vyos_vlan:
vlan_id: 100 vlan_id: 100
@ -166,6 +186,7 @@ def map_params_to_obj(module):
d = item.copy() d = item.copy()
d['vlan_id'] = str(d['vlan_id']) d['vlan_id'] = str(d['vlan_id'])
module._check_required_one_of(module.required_one_of, item)
obj.append(d) obj.append(d)
else: else:
@ -174,7 +195,8 @@ def map_params_to_obj(module):
'name': module.params['name'], 'name': module.params['name'],
'address': module.params['address'], 'address': module.params['address'],
'state': module.params['state'], 'state': module.params['state'],
'interfaces': module.params['interfaces'] 'interfaces': module.params['interfaces'],
'associated_interfaces': module.params['associated_interfaces']
}) })
return obj return obj
@ -213,25 +235,33 @@ def map_config_to_obj(module):
return objs return objs
def check_declarative_intent_params(want, module): def check_declarative_intent_params(want, module, result):
if module.params['interfaces']:
time.sleep(module.params['delay'])
have = map_config_to_obj(module)
want_interface = list() have = None
obj_interface = list() obj_interface = list()
is_delay = False
for w in want: for w in want:
for i in w['interfaces']: if w.get('associated_interfaces') is None:
want_interface.append(i) 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)
obj_in_have = search_obj_in_list(w['vlan_id'], have) obj_in_have = search_obj_in_list(w['vlan_id'], have)
if obj_in_have: if obj_in_have:
for obj in obj_in_have: for obj in obj_in_have:
obj_interface.extend(obj['interfaces']) obj_interface.extend(obj['interfaces'])
for w in want: for w in want:
for i in w['interfaces']: if w.get('associated_interfaces') is None:
if (set(obj_interface) - set(want_interface)) != set([]): 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'])) module.fail_json(msg='Interface {0} not configured on vlan {1}'.format(i, w['vlan_id']))
@ -242,7 +272,8 @@ def main():
vlan_id=dict(type='int', required=True), vlan_id=dict(type='int', required=True),
name=dict(), name=dict(),
address=dict(), address=dict(),
interfaces=dict(type='list', required=True), interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'), delay=dict(default=10, type='int'),
state=dict(default='present', state=dict(default='present',
choices=['present', 'absent']) choices=['present', 'absent'])
@ -261,7 +292,9 @@ def main():
argument_spec.update(element_spec) argument_spec.update(element_spec)
argument_spec.update(vyos_argument_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']] mutually_exclusive = [['vlan_id', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,
@ -285,10 +318,10 @@ def main():
load_config(module, commands, commit=commit) load_config(module, commands, commit=commit)
result['changed'] = True result['changed'] = True
if result['changed']: check_declarative_intent_params(want, module, result)
check_declarative_intent_params(want, module)
module.exit_json(**result) module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -118,13 +118,16 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033 # Ensure sessions contains epoc. Will fail after 18th May 2033
- "'ansible_1' in result.session_name" - "'ansible_1' in result.session_name"
- name: Add interfaces to vlan - name: Add interfaces to vlan and check state
eos_vlan: eos_vlan:
vlan_id: 4000 vlan_id: 4000
state: present state: present
interfaces: interfaces:
- Ethernet1 - Ethernet1
- Ethernet2 - Ethernet2
associated_interfaces:
- Ethernet1
- Ethernet2
authorize: yes authorize: yes
provider: "{{ cli }}" provider: "{{ cli }}"
become: yes become: yes
@ -160,6 +163,22 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033 # Ensure sessions contains epoc. Will fail after 18th May 2033
- "result.session_name is not defined" - "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 - name: Remove interface from vlan
eos_vlan: eos_vlan:
vlan_id: 4000 vlan_id: 4000

@ -85,7 +85,7 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033 # Ensure sessions contains epoc. Will fail after 18th May 2033
- "result.session_name is not defined" - "result.session_name is not defined"
- name: Add Ethernet2 to vrf - name: Add Ethernet2 to vrf and check interface assigned state
eos_vrf: eos_vrf:
name: test name: test
rd: 1:201 rd: 1:201
@ -93,6 +93,8 @@
authorize: yes authorize: yes
interfaces: interfaces:
- Ethernet2 - Ethernet2
associated_interfaces:
- Ethernet2
provider: "{{ cli }}" provider: "{{ cli }}"
become: yes become: yes
register: result register: result
@ -124,6 +126,22 @@
# Ensure sessions contains epoc. Will fail after 18th May 2033 # Ensure sessions contains epoc. Will fail after 18th May 2033
- "'session_name' not in result.commands" - "'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 - name: Add multiple interfaces to vrf
eos_vrf: eos_vrf:
name: test1 name: test1

@ -72,6 +72,31 @@
that: that:
- "result.changed == false" - "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 - name: Remove interface from vlan
ios_vlan: &single_int ios_vlan: &single_int
vlan_id: 100 vlan_id: 100

@ -29,12 +29,15 @@
vlan_id: 100 vlan_id: 100
provider: "{{ connection }}" provider: "{{ connection }}"
- name: Add interfaces to vlan - name: Add interfaces to vlan and check intent (config + intent)
nxos_vlan: &interfaces nxos_vlan: &interfaces
vlan_id: 100 vlan_id: 100
interfaces: interfaces:
- "{{ testint1 }}" - "{{ testint1 }}"
- "{{ testint2 }}" - "{{ testint2 }}"
associated_interfaces:
- "{{ testint1 }}"
- "{{ testint2 }}"
provider: "{{ connection }}" provider: "{{ connection }}"
register: result register: result
@ -58,6 +61,32 @@
that: that:
- 'result.changed == false' - '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 - name: Remove interface from vlan
nxos_vlan: &single_int nxos_vlan: &single_int
vlan_id: 100 vlan_id: 100

@ -64,18 +64,22 @@
that: that:
- "result.changed == false" - "result.changed == false"
- name: Assign interfaces to VRF - name: Assign interfaces to VRF (Config + intent)
nxos_vrf: &interfaces nxos_vrf: &interfaces
name: test1 name: test1
interfaces: interfaces:
- "{{ testint1 }}" - "{{ testint1 }}"
- "{{ testint2 }}" - "{{ testint2 }}"
associated_interfaces:
- "{{ testint1 }}"
- "{{ testint2 }}"
provider: "{{ connection }}" provider: "{{ connection }}"
register: result register: result
- assert: - assert:
that: that:
- 'result.changed == true' - 'result.changed == true'
- "result.failed == false"
- '"interface {{ testint1 }}" in result.commands' - '"interface {{ testint1 }}" in result.commands'
- '"vrf member test1" in result.commands' - '"vrf member test1" in result.commands'
- '"interface {{ testint2 }}" in result.commands' - '"interface {{ testint2 }}" in result.commands'
@ -89,6 +93,32 @@
that: that:
- 'result.changed == false' - '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 - name: Remove interface from vrf
nxos_vrf: &single_int nxos_vrf: &single_int
name: test1 name: test1

@ -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 }}"
Loading…
Cancel
Save