From 12460dd713b3d0dcdb577017edd89e1398336a8e Mon Sep 17 00:00:00 2001 From: Trishna Guha Date: Mon, 14 Aug 2017 12:27:03 +0530 Subject: [PATCH] Add ios_vrf declarative intent config check (#28001) * Add ios_vrf declarative intent config check Signed-off-by: Trishna Guha * add version for delay param * modify ios_vrf unit test --- lib/ansible/modules/network/ios/ios_vrf.py | 62 +++++++++++++++++-- .../units/modules/network/ios/test_ios_vrf.py | 5 ++ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/lib/ansible/modules/network/ios/ios_vrf.py b/lib/ansible/modules/network/ios/ios_vrf.py index dc0fc0c20ef..0b386300cd7 100644 --- a/lib/ansible/modules/network/ios/ios_vrf.py +++ b/lib/ansible/modules/network/ios/ios_vrf.py @@ -62,6 +62,11 @@ 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. + delay: + description: + - Time in seconds to wait before checking for the operational state on remote + device. + version_added: "2.4" purge: description: - Instructs the module to consider the @@ -127,16 +132,37 @@ delta: sample: "0:00:10.469466" """ import re - +import time from functools import partial from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import exec_command from ansible.module_utils.ios import load_config, get_config from ansible.module_utils.ios import ios_argument_spec, check_args from ansible.module_utils.netcfg import NetworkConfig from ansible.module_utils.six import iteritems +def get_interface_type(interface): + + if interface.upper().startswith('ET'): + return 'ethernet' + elif interface.upper().startswith('VL'): + return 'svi' + elif interface.upper().startswith('LO'): + return 'loopback' + elif interface.upper().startswith('MG'): + return 'management' + elif interface.upper().startswith('MA'): + return 'management' + elif interface.upper().startswith('PO'): + return 'portchannel' + elif interface.upper().startswith('NV'): + return 'nve' + else: + return 'unknown' + + def add_command_to_vrf(name, cmd, commands): if 'vrf definition %s' % name not in commands: commands.append('vrf definition %s' % name) @@ -149,7 +175,8 @@ def map_obj_to_commands(updates, module): for update in updates: want, have = update - needs_update = lambda x: want.get(x) and (want.get(x) != have.get(x)) + def needs_update(want, have, x): + return want.get(x) and (want.get(x) != have.get(x)) if want['state'] == 'absent': commands.append('no vrf definition %s' % want['name']) @@ -158,11 +185,11 @@ def map_obj_to_commands(updates, module): if not have.get('state'): commands.append('vrf definition %s' % want['name']) - if needs_update('description'): + if needs_update(want, have, 'description'): cmd = 'description %s' % want['description'] add_command_to_vrf(want['name'], cmd, commands) - if needs_update('rd'): + if needs_update(want, have, 'rd'): cmd = 'rd %s' % want['rd'] add_command_to_vrf(want['name'], cmd, commands) @@ -300,18 +327,36 @@ def update_objects(want, have): updates.append((entry, item)) return updates + +def check_declarative_intent_params(want, module): + if module.params['interfaces']: + name = module.params['name'] + rc, out, err = exec_command(module, 'show vrf | include {0}'.format(name)) + + if rc == 0: + data = out.strip().split() + vrf = data[0] + interface = data[-1] + + for w in want: + if w['name'] == vrf: + for i in w['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)) + + def main(): """ main entry point for module execution """ argument_spec = dict( vrfs=dict(type='list'), - name=dict(), + name=dict(), description=dict(), rd=dict(), - interfaces=dict(type='list'), + delay=dict(default=10, type='int'), purge=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent']) ) @@ -350,6 +395,11 @@ def main(): load_config(module, commands) result['changed'] = True + if result['changed']: + time.sleep(module.params['delay']) + + check_declarative_intent_params(want, module) + module.exit_json(**result) if __name__ == '__main__': diff --git a/test/units/modules/network/ios/test_ios_vrf.py b/test/units/modules/network/ios/test_ios_vrf.py index aeced620ea4..61d1ce9e8fc 100644 --- a/test/units/modules/network/ios/test_ios_vrf.py +++ b/test/units/modules/network/ios/test_ios_vrf.py @@ -38,12 +38,17 @@ class TestIosVrfModule(TestIosModule): self.mock_load_config = patch('ansible.modules.network.ios.ios_vrf.load_config') self.load_config = self.mock_load_config.start() + self.mock_exec_command = patch('ansible.modules.network.ios.ios_vrf.exec_command') + self.exec_command = self.mock_exec_command.start() + def tearDown(self): self.mock_get_config.stop() self.mock_load_config.stop() + self.mock_exec_command.stop() def load_fixtures(self, commands=None): self.get_config.return_value = load_fixture('ios_vrf_config.cfg') + self.exec_command.return_value = (0, load_fixture('ios_vrf_config.cfg').strip(), None) self.load_config.return_value = None def test_ios_vrf_name(self):