Add aggregate functionality to eos_vrf (#27915)
* Add aggregate functionality to eos_vrf * Add tests for eos_vrf aggregate option * Remove test2 and test3 vrfs at the beginning of the eos_vrf tests * Pull all vrfs With aggregate, we need to get all VRFs and we then compare with desired VRFs, instead of assuming it will be just one.
This commit is contained in:
parent
eee09565b1
commit
7e2169f6d5
2 changed files with 188 additions and 54 deletions
|
@ -81,100 +81,152 @@ import re
|
|||
import time
|
||||
|
||||
|
||||
def search_obj_in_list(name, lst):
|
||||
for o in lst:
|
||||
if o['name'] == name:
|
||||
return o
|
||||
|
||||
|
||||
def map_obj_to_commands(updates, module):
|
||||
commands = list()
|
||||
want, have = updates
|
||||
state = module.params['state']
|
||||
|
||||
if state == 'absent':
|
||||
if have:
|
||||
commands.append('no vrf definition %s' % want['name'])
|
||||
elif state == 'present':
|
||||
if not have:
|
||||
commands.append('vrf definition %s' % want['name'])
|
||||
for w in want:
|
||||
name = w['name']
|
||||
rd = w['rd']
|
||||
interfaces = w['interfaces']
|
||||
|
||||
if want['rd'] is not None:
|
||||
commands.append('rd %s' % want['rd'])
|
||||
obj_in_have = search_obj_in_list(name, have)
|
||||
|
||||
if want['interfaces']:
|
||||
for i in want['interfaces']:
|
||||
commands.append('interface %s' % i)
|
||||
commands.append('vrf forwarding %s' % want['name'])
|
||||
else:
|
||||
if want['rd'] is not None and want['rd'] != have['rd']:
|
||||
commands.append('vrf definition %s' % want['name'])
|
||||
commands.append('rd %s' % want['rd'])
|
||||
if state == 'absent':
|
||||
if have:
|
||||
commands.append('no vrf definition %s' % name)
|
||||
elif state == 'present':
|
||||
if not obj_in_have:
|
||||
commands.append('vrf definition %s' % name)
|
||||
|
||||
if want['interfaces']:
|
||||
if not have['interfaces']:
|
||||
for i in want['interfaces']:
|
||||
if rd is not None:
|
||||
commands.append('rd %s' % rd)
|
||||
|
||||
if w['interfaces']:
|
||||
for i in w['interfaces']:
|
||||
commands.append('interface %s' % i)
|
||||
commands.append('vrf forwarding %s' % want['name'])
|
||||
elif set(want['interfaces']) != have['interfaces']:
|
||||
missing_interfaces = list(set(want['interfaces']) - set(have['interfaces']))
|
||||
commands.append('vrf forwarding %s' % w['name'])
|
||||
else:
|
||||
if w['rd'] is not None and w['rd'] != obj_in_have['rd']:
|
||||
commands.append('vrf definition %s' % w['name'])
|
||||
commands.append('rd %s' % w['rd'])
|
||||
|
||||
for i in missing_interfaces:
|
||||
commands.append('interface %s' % i)
|
||||
commands.append('vrf forwarding %s' % want['name'])
|
||||
if w['interfaces']:
|
||||
if not obj_in_have['interfaces']:
|
||||
for i in w['interfaces']:
|
||||
commands.append('interface %s' % i)
|
||||
commands.append('vrf forwarding %s' % w['name'])
|
||||
elif set(w['interfaces']) != obj_in_have['interfaces']:
|
||||
missing_interfaces = list(set(w['interfaces']) - set(obj_in_have['interfaces']))
|
||||
|
||||
for i in missing_interfaces:
|
||||
commands.append('interface %s' % i)
|
||||
commands.append('vrf forwarding %s' % w['name'])
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def map_config_to_obj(module):
|
||||
obj = {}
|
||||
output = run_commands(module, ['show vrf %s' % module.params['name']])
|
||||
lines = output[0].strip().splitlines()
|
||||
objs = []
|
||||
output = run_commands(module, ['show vrf'])
|
||||
lines = output[0].strip().splitlines()[2:]
|
||||
|
||||
if len(lines) > 2:
|
||||
splitted_line = re.split(r'\s{2,}', lines[2].strip())
|
||||
obj['name'] = splitted_line[0]
|
||||
obj['rd'] = splitted_line[1]
|
||||
obj['interfaces'] = None
|
||||
for l in lines:
|
||||
if not l:
|
||||
continue
|
||||
|
||||
if len(splitted_line) > 4:
|
||||
obj['interfaces'] = []
|
||||
for i in splitted_line[4].split(','):
|
||||
obj['interfaces'].append(i.strip())
|
||||
splitted_line = re.split(r'\s{2,}', l.strip())
|
||||
|
||||
if len(splitted_line) == 1:
|
||||
continue
|
||||
else:
|
||||
obj = {}
|
||||
obj['name'] = splitted_line[0]
|
||||
obj['rd'] = splitted_line[1]
|
||||
obj['interfaces'] = None
|
||||
|
||||
if len(splitted_line) > 4:
|
||||
obj['interfaces'] = []
|
||||
|
||||
for i in splitted_line[4].split(','):
|
||||
obj['interfaces'].append(i.strip())
|
||||
|
||||
objs.append(obj)
|
||||
|
||||
return objs
|
||||
|
||||
|
||||
def map_params_to_obj(module):
|
||||
obj = []
|
||||
|
||||
if 'aggregate' in module.params and module.params['aggregate']:
|
||||
for c in module.params['aggregate']:
|
||||
d = c.copy()
|
||||
|
||||
if 'state' not in d:
|
||||
d['state'] = module.params['state']
|
||||
if 'rd' not in d:
|
||||
d['rd'] = module.params['rd']
|
||||
if 'interfaces' not in d:
|
||||
d['interfaces'] = module.params['interfaces']
|
||||
|
||||
obj.append(d)
|
||||
else:
|
||||
name = module.params['name'],
|
||||
state = module.params['state'],
|
||||
rd = module.params['rd'],
|
||||
interfaces = module.params['interfaces']
|
||||
|
||||
obj.append({
|
||||
'name': module.params['name'],
|
||||
'state': module.params['state'],
|
||||
'rd': module.params['rd'],
|
||||
'interfaces': module.params['interfaces']
|
||||
})
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def map_params_to_obj(module):
|
||||
return {
|
||||
'name': module.params['name'],
|
||||
'state': module.params['state'],
|
||||
'rd': module.params['rd'],
|
||||
'interfaces': module.params['interfaces']
|
||||
}
|
||||
|
||||
|
||||
def check_declarative_intent_params(module):
|
||||
def check_declarative_intent_params(want, module):
|
||||
if module.params['interfaces']:
|
||||
time.sleep(module.params['delay'])
|
||||
have = map_config_to_obj(module)
|
||||
vrf = module.params['name']
|
||||
|
||||
for i in module.params['interfaces']:
|
||||
if i not in have['interfaces']:
|
||||
module.fail_json(msg="Interface %s not configured on vrf %s" % (i, vrf))
|
||||
for w in want:
|
||||
for i in w['interfaces']:
|
||||
obj_in_have = search_obj_in_list(w['name'], 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 vrf %s" % (i, w['name']))
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
name=dict(required=True),
|
||||
name=dict(),
|
||||
interfaces=dict(type='list'),
|
||||
delay=dict(default=10, type='int'),
|
||||
rd=dict(),
|
||||
aggregate=dict(),
|
||||
aggregate=dict(type='list'),
|
||||
purge=dict(default=False, type='bool'),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(eos_argument_spec)
|
||||
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
|
@ -200,7 +252,7 @@ def main():
|
|||
result['changed'] = True
|
||||
|
||||
if result['changed']:
|
||||
check_declarative_intent_params(module)
|
||||
check_declarative_intent_params(want, module)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
|
|
@ -7,6 +7,20 @@
|
|||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: setup - remove vrf
|
||||
eos_vrf:
|
||||
name: test2
|
||||
state: absent
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: setup - remove vrf
|
||||
eos_vrf:
|
||||
name: test3
|
||||
state: absent
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: Create vrf
|
||||
eos_vrf:
|
||||
name: test
|
||||
|
@ -110,6 +124,74 @@
|
|||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'session_name' not in result.commands"
|
||||
|
||||
- name: Create aggregate of VRFs
|
||||
eos_vrf:
|
||||
aggregate:
|
||||
- { name: test2, rd: "1:202" }
|
||||
- { name: test3, rd: "1:203" }
|
||||
state: present
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'vrf definition test2' in result.commands"
|
||||
- "'rd 1:202' in result.commands"
|
||||
- "'vrf definition test3' in result.commands"
|
||||
- "'rd 1:203' in result.commands"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
- name: Create aggregate of VRFs again (idempotent)
|
||||
eos_vrf:
|
||||
aggregate:
|
||||
- { name: test2, rd: "1:202" }
|
||||
- { name: test3, rd: "1:203" }
|
||||
state: present
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.commands | length == 0"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "result.session_name is not defined"
|
||||
|
||||
- name: Delete VRFs
|
||||
eos_vrf:
|
||||
name: test
|
||||
state: absent
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: Delete VRFs again (idempotent)
|
||||
eos_vrf:
|
||||
name: test
|
||||
state: absent
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: Delete aggregate of VRFs
|
||||
eos_vrf:
|
||||
aggregate:
|
||||
- { name: test2 }
|
||||
- { name: test3 }
|
||||
state: absent
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- name: Delete VRFs again (idempotent)
|
||||
eos_vrf:
|
||||
aggregate:
|
||||
- { name: test2 }
|
||||
- { name: test3 }
|
||||
state: absent
|
||||
authorize: yes
|
||||
provider: "{{ cli }}"
|
||||
|
||||
# FIXME add in tests for everything defined in docs
|
||||
# FIXME Test state:absent + test:
|
||||
|
|
Loading…
Reference in a new issue