Skip to content

Commit

Permalink
Devel ios vrf submode (ansible#41659)
Browse files Browse the repository at this point in the history
* add submode features ipv4/ipv6

* add tests for submode

* the parameters both_ * were not idempotent, in what is read in the configuration we have a list with 2 entries (import, export) while the input parameter has only one parameter which will be applied twice

* add docstring to the ios_vrf module + provide a fix for: ansible#41581

* complete tests

* add missing description

* fix KeyError for address-family ipv*

* fix both_* tests

* fix E231, W292, W293

* fix W293

* remove set has it doesn't preserve order of routes

* fix E106

* remove ImportError: cannot import name OrderedDict

* We should be able to mix the parameters for the routes targets , while remaining imdepotent. During the first implementation we did not take this into account, which did not correspond to the reality of the needs in production (to be able to use each parameter indifemently together)

* remove epdb reference

* FIX E111, E106

* FIX E302

* using loop produce a result who  was not imdepotent

* FIX E241

* fix: used pass intead of list
  • Loading branch information
clementtrebuchet authored and ganeshrn committed Jul 12, 2018
1 parent 2038ff5 commit 7c97bb5
Show file tree
Hide file tree
Showing 3 changed files with 350 additions and 27 deletions.
234 changes: 214 additions & 20 deletions lib/ansible/modules/network/ios/ios_vrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
Expand Down Expand Up @@ -103,6 +102,30 @@
description:
- Adds an import list of extended route target communities to the VRF.
version_added: "2.5"
route_both_ipv4:
description:
- Adds an export and import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_export_ipv4:
description:
- Adds an export list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_import_ipv4:
description:
- Adds an import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_both_ipv6:
description:
- Adds an export and import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_export_ipv6:
description:
- Adds an export list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_import_ipv6:
description:
- Adds an import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
"""
EXAMPLES = """
Expand Down Expand Up @@ -134,6 +157,22 @@
- 1:100
- 3:100
- name: Creates a list of import RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_import_ipv4
rd: 1:100
route_import_ipv4:
- 1:100
- 3:100
- name: Creates a list of import RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_import_ipv6
rd: 1:100
route_import_ipv6:
- 1:100
- 3:100
- name: Creates a list of export RTs for the VRF with the same parameters
ios_vrf:
name: test_export
Expand All @@ -142,13 +181,46 @@
- 1:100
- 3:100
- name: Creates a list of export RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_export_ipv4
rd: 1:100
route_export_ipv4:
- 1:100
- 3:100
- name: Creates a list of export RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_export_ipv6
rd: 1:100
route_export_ipv6:
- 1:100
- 3:100
- name: Creates a list of import and export route targets for the VRF with the same parameters
ios_vrf:
name: test_both
rd: 1:100
route_both:
- 1:100
- 3:100
- name: Creates a list of import and export route targets in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_both_ipv4
rd: 1:100
route_both_ipv4:
- 1:100
- 3:100
- name: Creates a list of import and export route targets in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_both_ipv6
rd: 1:100
route_both_ipv6:
- 1:100
- 3:100
"""

RETURN = """
Expand Down Expand Up @@ -220,12 +292,13 @@ def add_command_to_vrf(name, cmd, commands):

def map_obj_to_commands(updates, module):
commands = list()
state = module.params['state'] # FIXME NOT USED

for update in updates:
want, have = update

def needs_update(want, have, x):
if isinstance(want.get(x), list) and isinstance(have.get(x), list):
return want.get(x) and (want.get(x) != have.get(x)) and not all(elem in have.get(x) for elem in want.get(x))
return want.get(x) and (want.get(x) != have.get(x))

if want['state'] == 'absent':
Expand Down Expand Up @@ -257,9 +330,40 @@ def needs_update(want, have, x):
cmd = 'route-target export %s' % route
add_command_to_vrf(want['name'], cmd, commands)

if needs_update(want, have, 'route_both'):
for route in want['route_both']:
cmd = 'route-target both %s' % route
if needs_update(want, have, 'route_import_ipv4'):
cmd = 'address-family ipv4'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_import_ipv4']:
cmd = 'route-target import %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)

if needs_update(want, have, 'route_export_ipv4'):
cmd = 'address-family ipv4'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_export_ipv4']:
cmd = 'route-target export %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)

if needs_update(want, have, 'route_import_ipv6'):
cmd = 'address-family ipv6'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_import_ipv6']:
cmd = 'route-target import %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)

if needs_update(want, have, 'route_export_ipv6'):
cmd = 'address-family ipv6'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_export_ipv6']:
cmd = 'route-target export %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)

if want['interfaces'] is not None:
Expand Down Expand Up @@ -324,15 +428,77 @@ def parse_export(configobj, name):
return matches


def parse_both(configobj, name):
def parse_both(configobj, name, address_family='global'):
rd_pattern = re.compile('(?P<rd>.+:.+)')
matches = list()
export_match = parse_export(configobj, name)
import_match = parse_import(configobj, name)
matches.extend(export_match)
matches.extend(import_match)
export_match = None
import_match = None
if address_family == "global":
export_match = parse_export(configobj, name)
import_match = parse_import(configobj, name)
elif address_family == "ipv4":
export_match = parse_export_ipv4(configobj, name)
import_match = parse_import_ipv4(configobj, name)
elif address_family == "ipv6":
export_match = parse_export_ipv6(configobj, name)
import_match = parse_import_ipv6(configobj, name)
if import_match and export_match:
for ex in export_match:
exrd = rd_pattern.search(ex)
exrd = exrd.groupdict().get('rd')
for im in import_match:
imrd = rd_pattern.search(im)
imrd = imrd.groupdict().get('rd')
if exrd == imrd:
matches.extend([exrd]) if exrd not in matches else None
matches.extend([imrd]) if imrd not in matches else None
return matches


def parse_import_ipv4(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv4']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+import\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass


def parse_export_ipv4(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv4']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+export\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass


def parse_import_ipv6(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv6']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+import\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass


def parse_export_ipv6(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv6']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+export\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass


def map_config_to_obj(module):
config = get_config(module)
configobj = NetworkConfig(indent=1, contents=config)
Expand All @@ -341,7 +507,6 @@ def map_config_to_obj(module):
return list()

instances = list()

for item in set(match):
obj = {
'name': item,
Expand All @@ -351,7 +516,13 @@ def map_config_to_obj(module):
'interfaces': parse_interfaces(configobj, item),
'route_import': parse_import(configobj, item),
'route_export': parse_export(configobj, item),
'route_both': parse_both(configobj, item)
'route_both': parse_both(configobj, item),
'route_import_ipv4': parse_import_ipv4(configobj, item),
'route_export_ipv4': parse_export_ipv4(configobj, item),
'route_both_ipv4': parse_both(configobj, item, address_family='ipv4'),
'route_import_ipv6': parse_import_ipv6(configobj, item),
'route_export_ipv6': parse_export_ipv6(configobj, item),
'route_both_ipv6': parse_both(configobj, item, address_family='ipv6'),
}
instances.append(obj)
return instances
Expand Down Expand Up @@ -396,7 +567,6 @@ def map_params_to_obj(module):
collection.append(item)

objects = list()

for item in collection:
get_value = partial(get_param_value, item=item, module=module)
item['description'] = get_value('description')
Expand All @@ -406,6 +576,21 @@ 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['route_import_ipv4'] = get_value('route_import_ipv4')
item['route_export_ipv4'] = get_value('route_export_ipv4')
item['route_both_ipv4'] = get_value('route_both_ipv4')
item['route_import_ipv6'] = get_value('route_import_ipv6')
item['route_export_ipv6'] = get_value('route_export_ipv6')
item['route_both_ipv6'] = get_value('route_both_ipv6')
both_addresses_family = ["", "_ipv6", "_ipv4"]
for address_family in both_addresses_family:
if item["route_both%s" % address_family]:
if not item["route_export%s" % address_family]:
item["route_export%s" % address_family] = list()
if not item["route_import%s" % address_family]:
item["route_import%s" % address_family] = list()
item["route_export%s" % address_family].extend(get_value("route_both%s" % address_family))
item["route_import%s" % address_family].extend(get_value("route_both%s" % address_family))
item['associated_interfaces'] = get_value('associated_interfaces')
objects.append(item)

Expand All @@ -421,13 +606,16 @@ def update_objects(want, have):
else:
for key, value in iteritems(entry):
if value:
if isinstance(value, list):
if sorted(value) != sorted(item[key]):
try:
if isinstance(value, list):
if sorted(value) != sorted(item[key]):
if (entry, item) not in updates:
updates.append((entry, item))
elif value != item[key]:
if (entry, item) not in updates:
updates.append((entry, item))
elif value != item[key]:
if (entry, item) not in updates:
updates.append((entry, item))
except TypeError:
pass
return updates


Expand Down Expand Up @@ -469,6 +657,13 @@ def main():
route_export=dict(type='list'),
route_import=dict(type='list'),
route_both=dict(type='list'),
route_export_ipv4=dict(type='list'),
route_import_ipv4=dict(type='list'),
route_both_ipv4=dict(type='list'),
route_export_ipv6=dict(type='list'),
route_import_ipv6=dict(type='list'),
route_both_ipv6=dict(type='list'),


interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
Expand All @@ -480,7 +675,7 @@ def main():

argument_spec.update(ios_argument_spec)

mutually_exclusive = [('name', 'vrfs'), ('route_import', 'route_both'), ('route_export', 'route_both')]
mutually_exclusive = [('name', 'vrfs')]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
Expand All @@ -493,7 +688,6 @@ def main():

want = map_params_to_obj(module)
have = map_config_to_obj(module)

commands = map_obj_to_commands(update_objects(want, have), module)

if module.params['purge']:
Expand Down
Loading

0 comments on commit 7c97bb5

Please sign in to comment.