From 668778874a73781a7921ae44ca972ae91f11d14e Mon Sep 17 00:00:00 2001 From: GGabriele Date: Thu, 15 Sep 2016 16:25:32 +0200 Subject: [PATCH] Adding nxos_pim_interface --- network/nxos/nxos_pim_interface.py | 929 +++++++++++++++++++++++++++++ 1 file changed, 929 insertions(+) create mode 100644 network/nxos/nxos_pim_interface.py diff --git a/network/nxos/nxos_pim_interface.py b/network/nxos/nxos_pim_interface.py new file mode 100644 index 00000000000..42413f537de --- /dev/null +++ b/network/nxos/nxos_pim_interface.py @@ -0,0 +1,929 @@ +#!/usr/bin/python +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# + +DOCUMENTATION = ''' +--- +module: nxos_pim_interface +version_added: "2.2" +short_description: Manages PIM interface configuration. +description: + - Manages PIM interface configuration settings. +extends_documentation_fragment: nxos +author: + - Jason Edelman (@jedelman8) +notes: + - When C(state=default), supported params will be reset to a default state. + These include C(dr_prio), C(hello_auth_key), C(hello_interval), C(jp_policy_out), + C(jp_policy_in), C(jp_type_in), C(jp_type_out), C(border), C(neighbor_policy), + C(neighbor_type). + - The C(hello_auth_key) param is not idempotent. + - C(hello_auth_key) only supports clear text passwords. + - When C(state=absent), pim interface configuration will be set to defaults and pim-sm + will be disabled on the interface. + - PIM must be enabled on the device to use this module. + - This module is for Layer 3 interfaces. +options: + interface: + description: + - Full name of the interface such as Ethernet1/33. + required: true + sparse: + description: + - Enable/disable sparse-mode on the interface. + required: false + default: true + choices: ['true', 'false'] + hello_auth_key: + description: + - Authentication for hellos on this interface. + required: false + default: null + hello_interval: + description: + - Hello interval in milliseconds for this interface. + required: false + default: null + choices: ['true', 'false'] + jp_policy_out: + description: + - Policy for join-prune messages (outbound). + required: true + default: null + jp_policy_in: + description: + - Policy for join-prune messages (inbound). + required: false + default: null + jp_type_out: + description: + - Type of policy mapped to C(jp_policy_out). + required: false + default: null + choices: ['prefix', 'routemap'] + jp_type_in: + description: + - Type of policy mapped to C(jp_policy_in). + required: false + default: null + choices: ['prefix', 'routemap'] + border: + description: + - Configures interface to be a boundary of a PIM domain. + required: false + default: null + choices: ['true', 'false'] + neighbor_policy: + description: + - Configures a neighbor policy for filtering adjacencies. + required: false + default: null + neighbor_type: + description: + - Type of policy mapped to neighbor_policy. + required: false + default: null + choices: ['prefix', 'routemap'] + state: + description: + - Manages desired state of the resource. + required: false + default: present + choices: ['present', 'default'] +''' +EXAMPLES = ''' +# ensure PIM is not running on the interface +- nxos_pim_interface: + interface=eth1/33 + state=absent + host={{ inventory_hostname }} + username={{ un }} + password={{ pwd }} + +# ensure the interface has pim-sm enabled with the appropriate priority and hello interval +- nxos_pim_interface: + interface=eth1/33 + dr_prio=10 + hello_interval=40 + state=present + host={{ inventory_hostname }} + username={{ un }} + password={{ pwd }} + +# ensure join-prune policies exist +- nxos_pim_interface: + interface=eth1/33 + jp_policy_in=JPIN + jp_policy_out=JPOUT + jp_type_in=routemap + jp_type_out=routemap + host={{ inventory_hostname }} + username={{ un }} + password={{ pwd }} + +# ensure defaults are in place +- nxos_pim_interface: + interface=eth1/33 + state=default + host={{ inventory_hostname }} + username={{ un }} + password={{ pwd }} +''' + +RETURN = ''' +proposed: + description: k/v pairs of parameters passed into module + returned: always + type: dict + sample: {"interface": "eth1/33", "neighbor_policy": "test", + "neighbor_type": "routemap", "sparse": true} +existing: + description: + - k/v pairs of existing configuration + type: dict + sample: {"border": false, "dr_prio": "1", "hello_interval": "30000", + "isauth": false, "jp_bidir": false, "jp_policy_in": "JPIN", + "jp_policy_out": "1", "jp_type_in": "routemap", + "jp_type_out": null, "neighbor_policy": "test1", + "neighbor_type": "prefix", "sparse": true} +end_state: + description: k/v pairs of configuration after module execution + returned: always + type: dict + sample: {"border": false, "dr_prio": "1", "hello_interval": "30000", + "isauth": false, "jp_bidir": false, "jp_policy_in": "JPIN", + "jp_policy_out": "1", "jp_type_in": "routemap", + "jp_type_out": null, "neighbor_policy": "test", + "neighbor_type": "routemap", "sparse": true} +updates: + description: command sent to the device + returned: always + type: list + sample: ["interface eth1/33", "ip pim neighbor-policy test", + "ip pim neighbor-policy test"] +changed: + description: check to see if a change was made on the device + returned: always + type: boolean + sample: true +''' + + +import json +import time + +# COMMON CODE FOR MIGRATION +import re + +from ansible.module_utils.basic import get_exception +from ansible.module_utils.netcfg import NetworkConfig, ConfigLine +from ansible.module_utils.shell import ShellError + +try: + from ansible.module_utils.nxos import get_module +except ImportError: + from ansible.module_utils.nxos import NetworkModule + + +def to_list(val): + if isinstance(val, (list, tuple)): + return list(val) + elif val is not None: + return [val] + else: + return list() + + +class CustomNetworkConfig(NetworkConfig): + + def expand_section(self, configobj, S=None): + if S is None: + S = list() + S.append(configobj) + for child in configobj.children: + if child in S: + continue + self.expand_section(child, S) + return S + + def get_object(self, path): + for item in self.items: + if item.text == path[-1]: + parents = [p.text for p in item.parents] + if parents == path[:-1]: + return item + + def to_block(self, section): + return '\n'.join([item.raw for item in section]) + + def get_section(self, path): + try: + section = self.get_section_objects(path) + return self.to_block(section) + except ValueError: + return list() + + def get_section_objects(self, path): + if not isinstance(path, list): + path = [path] + obj = self.get_object(path) + if not obj: + raise ValueError('path does not exist in config') + return self.expand_section(obj) + + + def add(self, lines, parents=None): + """Adds one or lines of configuration + """ + + ancestors = list() + offset = 0 + obj = None + + ## global config command + if not parents: + for line in to_list(lines): + item = ConfigLine(line) + item.raw = line + if item not in self.items: + self.items.append(item) + + else: + for index, p in enumerate(parents): + try: + i = index + 1 + obj = self.get_section_objects(parents[:i])[0] + ancestors.append(obj) + + except ValueError: + # add parent to config + offset = index * self.indent + obj = ConfigLine(p) + obj.raw = p.rjust(len(p) + offset) + if ancestors: + obj.parents = list(ancestors) + ancestors[-1].children.append(obj) + self.items.append(obj) + ancestors.append(obj) + + # add child objects + for line in to_list(lines): + # check if child already exists + for child in ancestors[-1].children: + if child.text == line: + break + else: + offset = len(parents) * self.indent + item = ConfigLine(line) + item.raw = line.rjust(len(line) + offset) + item.parents = ancestors + ancestors[-1].children.append(item) + self.items.append(item) + + +def get_network_module(**kwargs): + try: + return get_module(**kwargs) + except NameError: + return NetworkModule(**kwargs) + +def get_config(module, include_defaults=False): + config = module.params['config'] + if not config: + try: + config = module.get_config() + except AttributeError: + defaults = module.params['include_defaults'] + config = module.config.get_config(include_defaults=defaults) + return CustomNetworkConfig(indent=2, contents=config) + +def load_config(module, candidate): + config = get_config(module) + + commands = candidate.difference(config) + commands = [str(c).strip() for c in commands] + + save_config = module.params['save'] + + result = dict(changed=False) + + if commands: + if not module.check_mode: + try: + module.configure(commands) + except AttributeError: + module.config(commands) + + if save_config: + try: + module.config.save_config() + except AttributeError: + module.execute(['copy running-config startup-config']) + + result['changed'] = True + result['updates'] = commands + + return result +# END OF COMMON CODE + + +def execute_config_command(commands, module): + try: + module.configure(commands) + except ShellError: + clie = get_exception() + module.fail_json(msg='Error sending CLI commands', + error=str(clie), commands=commands) + except AttributeError: + try: + commands.insert(0, 'configure') + module.cli.add_commands(commands, output='config') + module.cli.run_commands() + except ShellError: + clie = get_exception() + module.fail_json(msg='Error sending CLI commands', + error=str(clie), commands=commands) + + +def get_cli_body_ssh(command, response, module, text=False): + """Get response for when transport=cli. This is kind of a hack and mainly + needed because these modules were originally written for NX-API. And + not every command supports "| json" when using cli/ssh. As such, we assume + if | json returns an XML string, it is a valid command, but that the + resource doesn't exist yet. Instead, the output will be a raw string + when issuing commands containing 'show run'. + """ + if 'xml' in response[0] or response[0] == '\n' or '^' in response[0]: + body = [] + elif 'show run' in command or text: + body = response + else: + try: + if isinstance(response[0], str): + body = [json.loads(response[0])] + else: + body = response + except ValueError: + module.fail_json(msg='Command does not support JSON output', + command=command) + return body + + +def execute_show(cmds, module, command_type=None): + command_type_map = { + 'cli_show': 'json', + 'cli_show_ascii': 'text' + } + + try: + if command_type: + response = module.execute(cmds, command_type=command_type) + else: + response = module.execute(cmds) + except ShellError: + clie = get_exception() + module.fail_json(msg='Error sending {0}'.format(cmds), + error=str(clie)) + except AttributeError: + try: + if command_type: + command_type = command_type_map.get(command_type) + module.cli.add_commands(cmds, output=command_type) + response = module.cli.run_commands() + else: + module.cli.add_commands(cmds) + response = module.cli.run_commands() + except ShellError: + clie = get_exception() + module.fail_json(msg='Error sending {0}'.format(cmds), + error=str(clie)) + return response + + +def execute_show_command(command, module, command_type='cli_show', text=False): + if module.params['transport'] == 'cli': + if 'show run' not in command and text is False: + command += ' | json' + cmds = [command] + response = execute_show(cmds, module) + body = get_cli_body_ssh(command, response, module, text=text) + elif module.params['transport'] == 'nxapi': + cmds = [command] + body = execute_show(cmds, module, command_type=command_type) + + return body + + +def flatten_list(command_lists): + flat_command_list = [] + for command in command_lists: + if isinstance(command, list): + flat_command_list.extend(command) + else: + flat_command_list.append(command) + return flat_command_list + + +def local_existing(gexisting): + jp_bidir = False + isauth = False + if gexisting: + jp_bidir = gexisting.get('jp_bidir') + isauth = gexisting.get('isauth') + if jp_bidir and isauth: + gexisting.pop('jp_bidir') + gexisting.pop('isauth') + gexisting['sparse'] = True + + return gexisting, jp_bidir, isauth + + +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' + else: + return 'unknown' + + +def get_interface_mode(interface, intf_type, module): + command = 'show interface {0}'.format(interface) + mode = 'unknown' + interface_table = {} + body = execute_show_command(command, module) + + try: + interface_table = body[0]['TABLE_interface']['ROW_interface'] + except (KeyError, AttributeError, IndexError): + return mode + + if intf_type in ['ethernet', 'portchannel']: + mode = str(interface_table.get('eth_mode', 'layer3')) + if mode in ['access', 'trunk']: + mode = 'layer2' + elif mode == 'routed': + mode = 'layer3' + elif intf_type in ['loopback', 'svi']: + mode = 'layer3' + return mode + + +def get_pim_interface(module, interface): + pim_interface = {} + command = 'show ip pim interface {0}'.format(interface) + + body = execute_show_command(command, module, + command_type='cli_show_ascii', text=True) + + if body: + if 'not running' not in body[0]: + body = execute_show_command(command, module) + + try: + get_data = body[0]['TABLE_iod']['ROW_iod'] + + if isinstance(get_data.get('dr-priority'), unicode) or \ + isinstance(get_data.get('dr-priority'), str): + pim_interface['dr_prio'] = get_data.get('dr-priority') + else: + pim_interface['dr_prio'] = get_data.get('dr-priority')[0] + + hello_interval = get_data.get('hello-interval-sec') + if hello_interval: + hello_interval_msec = int(get_data.get('hello-interval-sec'))*1000 + pim_interface['hello_interval'] = str(hello_interval_msec) + border = get_data.get('is-border') + + if border == 'true': + pim_interface['border'] = True + elif border == 'false': + pim_interface['border'] = False + + isauth = get_data.get('isauth-config') + if isauth == 'true': + pim_interface['isauth'] = True + elif isauth == 'false': + pim_interface['isauth'] = False + + pim_interface['neighbor_policy'] = get_data.get('nbr-policy-name') + if pim_interface['neighbor_policy'] == 'none configured': + pim_interface['neighbor_policy'] = None + + jp_in_policy = get_data.get('jp-in-policy-name') + pim_interface['jp_policy_in'] = jp_in_policy + if jp_in_policy == 'none configured': + pim_interface['jp_policy_in'] = None + + if isinstance(get_data.get('jp-out-policy-name'), unicode) or \ + isinstance(get_data.get('jp-out-policy-name'), str): + pim_interface['jp_policy_out'] = get_data.get('jp-out-policy-name') + else: + pim_interface['jp_policy_out'] = get_data.get( + 'jp-out-policy-name')[0] + + if pim_interface['jp_policy_out'] == 'none configured': + pim_interface['jp_policy_out'] = None + + except (KeyError, AttributeError, TypeError, IndexError): + return {} + + command = 'show run interface {0}'.format(interface) + + body = execute_show_command(command, module, command_type='cli_show_ascii') + + jp_configs = [] + neigh = None + if body: + all_lines = body[0].splitlines() + + for each in all_lines: + if 'jp-policy' in each: + jp_configs.append(str(each.strip())) + elif 'neighbor-policy' in each: + neigh = str(each) + + pim_interface['neighbor_type'] = None + neigh_type = None + if neigh: + if 'prefix-list' in neigh: + neigh_type = 'prefix' + else: + neigh_type = 'routemap' + pim_interface['neighbor_type'] = neigh_type + + len_existing = len(jp_configs) + list_of_prefix_type = len([x for x in jp_configs if 'prefix-list' in x]) + jp_type_in = None + jp_type_out = None + jp_bidir = False + if len_existing == 1: + # determine type + last_word = jp_configs[0].split(' ')[-1] + if last_word == 'in': + if list_of_prefix_type: + jp_type_in = 'prefix' + else: + jp_type_in = 'routemap' + elif last_word == 'out': + if list_of_prefix_type: + jp_type_out = 'prefix' + else: + jp_type_out = 'routemap' + else: + jp_bidir = True + if list_of_prefix_type: + jp_type_in = 'prefix' + jp_type_out = 'routemap' + else: + jp_type_in = 'routemap' + jp_type_out = 'routemap' + else: + for each in jp_configs: + last_word = each.split(' ')[-1] + if last_word == 'in': + if 'prefix-list' in each: + jp_type_in = 'prefix' + else: + jp_type_in = 'routemap' + elif last_word == 'out': + if 'prefix-list' in each: + jp_type_out = 'prefix' + else: + jp_type_out = 'routemap' + + pim_interface['jp_type_in'] = jp_type_in + pim_interface['jp_type_out'] = jp_type_out + pim_interface['jp_bidir'] = jp_bidir + + return pim_interface + + +def fix_delta(delta, existing): + if delta.get('sparse') is False and existing.get('sparse') is None: + delta.pop('sparse') + return delta + + +def config_pim_interface(delta, existing, jp_bidir, isauth): + command = None + commands = [] + + delta = fix_delta(delta, existing) + + CMDS = { + 'sparse': 'ip pim sparse-mode', + 'dr_prio': 'ip pim dr-priority {0}', + 'hello_interval': 'ip pim hello-interval {0}', + 'hello_auth_key': 'ip pim hello-authentication ah-md5 {0}', + 'border': 'ip pim border', + 'jp_policy_out': 'ip pim jp-policy prefix-list {0} out', + 'jp_policy_in': 'ip pim jp-policy prefix-list {0} in', + 'jp_type_in': '', + 'jp_type_out': '', + 'neighbor_policy': 'ip pim neighbor-policy prefix-list {0}', + 'neighbor_type': '' + } + + if jp_bidir: + if delta.get('jp_policy_in') or delta.get('jp_policy_out'): + if existing.get('jp_type_in') == 'prefix': + command = 'no ip pim jp-policy prefix-list {0}'.format( + existing.get('jp_policy_in') + ) + else: + command = 'no ip pim jp-policy {0}'.format( + existing.get('jp_policy_in') + ) + if command: + commands.append(command) + + for k, v in delta.iteritems(): + if k in ['dr_prio', 'hello_interval', 'hello_auth_key', 'border', + 'sparse']: + if v: + command = CMDS.get(k).format(v) + elif k == 'hello_auth_key': + if isauth: + command = 'no ip pim hello-authentication ah-md5' + else: + command = 'no ' + CMDS.get(k).format(v) + + if command: + commands.append(command) + elif k in ['neighbor_policy', 'jp_policy_in', 'jp_policy_out', + 'neighbor_type']: + if k in ['neighbor_policy', 'neighbor_type']: + temp = delta.get('neighbor_policy') or existing.get( + 'neighbor_policy') + if delta.get('neighbor_type') == 'prefix': + command = CMDS.get(k).format(temp) + elif delta.get('neighbor_type') == 'routemap': + command = 'ip pim neighbor-policy {0}'.format(temp) + elif existing.get('neighbor_type') == 'prefix': + command = CMDS.get(k).format(temp) + elif existing.get('neighbor_type') == 'routemap': + command = 'ip pim neighbor-policy {0}'.format(temp) + elif k in ['jp_policy_in', 'jp_type_in']: + temp = delta.get('jp_policy_in') or existing.get( + 'jp_policy_in') + if delta.get('jp_type_in') == 'prefix': + command = CMDS.get(k).format(temp) + elif delta.get('jp_type_in') == 'routemap': + command = 'ip pim jp-policy {0} in'.format(temp) + elif existing.get('jp_type_in') == 'prefix': + command = CMDS.get(k).format(temp) + elif existing.get('jp_type_in') == 'routemap': + command = 'ip pim jp-policy {0} in'.format(temp) + elif k in ['jp_policy_out', 'jp_type_out']: + temp = delta.get('jp_policy_out') or existing.get( + 'jp_policy_out') + if delta.get('jp_type_out') == 'prefix': + command = CMDS.get(k).format(temp) + elif delta.get('jp_type_out') == 'routemap': + command = 'ip pim jp-policy {0} out'.format(temp) + elif existing.get('jp_type_out') == 'prefix': + command = CMDS.get(k).format(temp) + elif existing.get('jp_type_out') == 'routemap': + command = 'ip pim jp-policy {0} out'.format(temp) + if command: + commands.append(command) + command = None + + return commands + + +def get_pim_interface_defaults(): + dr_prio = '1' + border = False + hello_interval = '30000' + hello_auth_key = False + + args = dict(dr_prio=dr_prio, border=border, + hello_interval=hello_interval, + hello_auth_key=hello_auth_key) + + default = dict((param, value) for (param, value) in args.iteritems() + if value is not None) + + return default + + +def default_pim_interface_policies(existing, jp_bidir): + commands = [] + + if jp_bidir: + if existing.get('jp_policy_in') or existing.get('jp_policy_out'): + if existing.get('jp_type_in') == 'prefix': + command = 'no ip pim jp-policy prefix-list {0}'.format( + existing.get('jp_policy_in') + ) + if command: + commands.append(command) + + elif not jp_bidir: + command = None + for k, v in existing.iteritems(): + if k == 'jp_policy_in': + if existing.get('jp_policy_in'): + if existing.get('jp_type_in') == 'prefix': + command = 'no ip pim jp-policy prefix-list {0} in'.format( + existing.get('jp_policy_in') + ) + else: + command = 'no ip pim jp-policy {0} in'.format( + existing.get('jp_policy_in') + ) + elif k == 'jp_policy_out': + if existing.get('jp_policy_out'): + if existing.get('jp_type_out') == 'prefix': + command = 'no ip pim jp-policy prefix-list {0} out'.format( + existing.get('jp_policy_out') + ) + else: + command = 'no ip pim jp-policy {0} out'.format( + existing.get('jp_policy_out') + ) + if command: + commands.append(command) + command = None + + if existing.get('neighbor_policy'): + command = 'no ip pim neighbor-policy' + commands.append(command) + + return commands + + +def config_pim_interface_defaults(existing, jp_bidir, isauth): + command = [] + + # returns a dict + defaults = get_pim_interface_defaults() + delta = dict(set(defaults.iteritems()).difference( + existing.iteritems())) + if delta: + # returns a list + command = config_pim_interface(delta, existing, + jp_bidir, isauth) + comm = default_pim_interface_policies(existing, jp_bidir) + if comm: + for each in comm: + command.append(each) + + return command + + +def main(): + argument_spec=dict( + interface=dict(required=True), + sparse=dict(type='bool', default=True), + dr_prio=dict(), + hello_auth_key=dict(), + hello_interval=dict(type='int'), + jp_policy_out=dict(), + jp_policy_in=dict(), + jp_type_out=dict(choices=['prefix', 'routemap']), + jp_type_in=dict(choices=['prefix', 'routemap']), + border=dict(type='bool'), + neighbor_policy=dict(), + neighbor_type=dict(choices=['prefix', 'routemap']), + state=dict(choices=['present', 'absent', 'default'], + default='present'), + ) + module = get_network_module(argument_spec=argument_spec, + supports_check_mode=True) + + state = module.params['state'] + + sparse = module.params['sparse'] + interface = module.params['interface'] + jp_type_in = module.params['jp_type_in'] + jp_type_out = module.params['jp_type_out'] + jp_policy_in = module.params['jp_policy_in'] + jp_policy_out = module.params['jp_policy_out'] + neighbor_policy = module.params['neighbor_policy'] + neighbor_type = module.params['neighbor_type'] + hello_interval = module.params['hello_interval'] + + intf_type = get_interface_type(interface) + if get_interface_mode(interface, intf_type, module) == 'layer2': + module.fail_json(msg='this module only works on Layer 3 interfaces.') + + if jp_policy_in: + if not jp_type_in: + module.fail_json(msg='jp_type_in required when using jp_policy_in.') + if jp_policy_out: + if not jp_type_out: + module.fail_json(msg='jp_type_out required when using ' + ' jp_policy_out.') + if neighbor_policy: + if not neighbor_type: + module.fail_json(msg='neighbor_type required when using ' + 'neighbor_policy.') + + get_existing = get_pim_interface(module, interface) + existing, jp_bidir, isauth = local_existing(get_existing) + end_state = existing + changed = False + + commands = [] + + args = [ + 'interface', + 'sparse', + 'dr_prio', + 'hello_auth_key', + 'hello_interval', + 'jp_policy_out', + 'jp_type_out', + 'jp_type_in', + 'jp_policy_in', + 'border', + 'neighbor_type', + 'neighbor_policy' + ] + proposed = dict((k, v) for k, v in module.params.iteritems() + if v is not None and k in args) + + ''' + CANNOT_ABSENT = ['dr_prio', 'hello_interval', + 'hello_auth_key', 'jp_policy_out', 'jp_policy_in', + 'jp_type_out', 'jp_type_in', 'border', 'neighbor_type', + 'neighbor_policy'] + ''' + + if hello_interval: + proposed['hello_interval'] = str(proposed['hello_interval'] * 1000) + + delta = dict(set(proposed.iteritems()).difference(existing.iteritems())) + + if state == 'present': + if delta: + command = config_pim_interface(delta, existing, jp_bidir, isauth) + if command: + commands.append(command) + elif state == 'default': + defaults = config_pim_interface_defaults(existing, jp_bidir, isauth) + if defaults: + commands.append(defaults) + + elif state == 'absent': + if existing.get('sparse') == True: + delta['sparse'] = False + # defaults is a list of commands + defaults = config_pim_interface_defaults(existing, jp_bidir, isauth) + if defaults: + commands.append(defaults) + + command = config_pim_interface(delta, existing, jp_bidir, isauth) + commands.append(command) + + if commands: + commands.insert(0, ['interface {0}'.format(interface)]) + + cmds = flatten_list(commands) + results = {} + if cmds: + if module.check_mode: + module.exit_json(changed=True, commands=cmds) + else: + changed = True + execute_config_command(cmds, module) + time.sleep(1) + get_existing = get_pim_interface(module, interface) + end_state, jp_bidir, isauth = local_existing(get_existing) + + results['proposed'] = proposed + results['existing'] = existing + results['updates'] = cmds + results['changed'] = changed + results['end_state'] = end_state + + module.exit_json(**results) + + +if __name__ == '__main__': + main() \ No newline at end of file