def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value and item == 'mtu': config[item] = int(value.strip("'")) elif value: config[item] = value.strip("'") else: config[item] = None if 'disable' in conf: config['enabled'] = False else: config['enabled'] = True return utils.remove_empties(config)
def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ protocol_conf = '\n'.join( filter(lambda x: ('legacy-protocols' in x), conf)) att_conf = '\n'.join( filter(lambda x: ('legacy-protocols' not in x), conf)) config = self.parse_attribs(['snmp', 'address'], att_conf) config['legacy_protocols'] = self.parse_protocols(protocol_conf) return utils.remove_empties(config)
def _state_deleted(want, have): """ The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] for x in [ k for k in have.get('system', {}) if k not in remove_empties(want.get('system', {})) ]: commands.append('no lacp system {0}'.format(x)) return commands
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config["name"] = conf["name"] config["enabled"] = bool(conf["enabled"]) return utils.remove_empties(config)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config['name'] = utils.parse_conf_arg(conf, 'interface') config['port_priority'] = utils.parse_conf_arg(conf, 'port-priority') config['rate'] = utils.parse_conf_arg(conf, 'rate') return utils.remove_empties(config)
def populate_facts(self, connection, ansible_facts, data=None): """ Populate the facts for static_routes :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not HAS_LXML: self._module.fail_json(msg='lxml is not installed.') if not HAS_XMLTODICT: self._module.fail_json(msg='xmltodict is not installed.') if not data: config_filter = """ <configuration> <routing-instances/> <routing-options/> </configuration> """ data = connection.get_configuration(filter=config_filter) if isinstance(data, string_types): data = etree.fromstring( to_bytes(data, errors='surrogate_then_replace')) resources = data.xpath('configuration/routing-options') vrf_resources = data.xpath('configuration/routing-instances') resources.extend(vrf_resources) objs = [] for resource in resources: if resource is not None: xml = self._get_xml_dict(resource) obj = self.render_config(self.generated_spec, xml) if obj: objs.append(obj) facts = {} if objs: facts['static_routes'] = [] params = utils.validate_config(self.argument_spec, {'config': objs}) for cfg in params['config']: facts['static_routes'].append(utils.remove_empties(cfg)) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) match = re.search(r'^(\S+)', conf) intf = match.group(1) if match.group(1).lower() == "preconfigure": match = re.search(r'^(\S+) (.*)', conf) if match: intf = match.group(2) if get_interface_type(intf) == 'unknown': return {} # populate the facts from the configuration config['name'] = intf # Get the configured IPV4 details ipv4 = [] ipv4_all = re.findall(r"ipv4 address (\S+.*)", conf) for each in ipv4_all: each_ipv4 = dict() if 'secondary' in each: each_ipv4['address'] = each.split(' secondary')[0] each_ipv4['secondary'] = True else: each_ipv4['address'] = each ipv4.append(each_ipv4) config['ipv4'] = ipv4 # Get the configured IPV6 details ipv6 = [] ipv6_all = re.findall(r"ipv6 address (\S+)", conf) for each in ipv6_all: each_ipv6 = dict() each_ipv6['address'] = each ipv6.append(each_ipv6) config['ipv6'] = ipv6 return utils.remove_empties(config)
def _state_merged(want, have): """ The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] updates = dict_diff(have, want) if updates: for key, value in iteritems( flatten_dict(remove_empties(updates['system']))): commands.append('lacp system {0} {1}'.format( key.replace('address', 'mac'), value)) return commands
def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ vif_conf = '\n'.join(filter(lambda x: ('vif' in x), conf)) eth_conf = '\n'.join(filter(lambda x: ('vif' not in x), conf)) config = self.parse_attribs( ['description', 'speed', 'mtu', 'duplex'], eth_conf) config['vifs'] = self.parse_vifs(vif_conf) return utils.remove_empties(config)
def populate_facts(self, connection, ansible_facts, data=None): """ Populate the facts for interfaces :param connection: the device connection :param data: previously collected configuration as lxml ElementTree root instance or valid xml sting :rtype: dictionary :returns: facts """ if not HAS_LXML: self._module.fail_json(msg='lxml is not installed.') if not data: config_filter = """ <configuration> <protocols> <lldp> <interface> </interface> </lldp> </protocols> </configuration> """ data = get_resource_config(connection, config_filter=config_filter) if isinstance(data, string_types): data = etree.fromstring( to_bytes(data, errors='surrogate_then_replace')) self._resources = data.xpath('configuration/protocols/lldp/interface') objs = [] for resource in self._resources: if resource is not None: obj = self.render_config(self.generated_spec, resource) if obj: objs.append(obj) facts = {} if objs: facts['lldp_interfaces'] = [] params = utils.validate_config(self.argument_spec, {'config': objs}) for cfg in params['config']: facts['lldp_interfaces'].append(utils.remove_empties(cfg)) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts
def render_config(self, spec, conf): """` Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The ElementTree instance of configuration object :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) enhanced_layer = True mode = utils.get_xml_conf_arg( conf, 'unit/family/ethernet-switching/interface-mode') if mode is None: mode = utils.get_xml_conf_arg( conf, 'unit/family/ethernet-switching/port-mode') enhanced_layer = False # Layer 2 is configured on interface if mode: config['name'] = utils.get_xml_conf_arg(conf, 'name') unit = utils.get_xml_conf_arg(conf, 'unit/name') config['unit'] = unit if unit else 0 config['enhanced_layer'] = enhanced_layer if mode == 'access': config['access'] = {} config['access']['vlan'] = utils.get_xml_conf_arg( conf, "unit/family/ethernet-switching/vlan/members") elif mode == 'trunk': config['trunk'] = {} vlan_members = conf.xpath( 'unit/family/ethernet-switching/vlan/members') if vlan_members: config['trunk']['allowed_vlans'] = [] for vlan_member in vlan_members: config['trunk']['allowed_vlans'].append( vlan_member.text) config['trunk']['native_vlan'] = utils.get_xml_conf_arg( conf, "native-vlan-id") return utils.remove_empties(config)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config['name'] = conf['name'] config['description'] = conf.get('description') config['unit'] = conf.get('unit', 0) config['ipv4'] = conf.get('ipv4') config['ipv6'] = conf.get('ipv6') return utils.remove_empties(config)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config["name"] = conf["name"] config["state"] = "suspend" if conf["status"] == "SUSPENDED" else conf[ "status"].lower() config["vlan_id"] = conf["vlan-id"] return utils.remove_empties(config)
def _state_deleted(want, have): """ The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] for key, value in iteritems( flatten_dict(dict_delete(have, remove_empties(want)))): commands.append( Lacp_interfaces._compute_commands(key, value, remove=True)) if commands: pad_commands(commands, have['name']) return commands
def render_config(self, spec, conf, vlan_info): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) if vlan_info == 'Name' and 'Name' not in conf: conf = list(filter(None, conf.split(' '))) config['vlan_id'] = int(conf[0]) config['name'] = conf[1] if len(conf[2].split('/')) > 1: if conf[2].split('/')[0] == 'sus': config['state'] = 'suspend' elif conf[2].split('/')[0] == 'act': config['state'] = 'active' config['shutdown'] = 'enabled' else: if conf[2] == 'suspended': config['state'] = 'suspend' elif conf[2] == 'active': config['state'] = 'active' config['shutdown'] = 'disabled' elif vlan_info == 'Type' and 'Type' not in conf: conf = list(filter(None, conf.split(' '))) config['mtu'] = int(conf[3]) elif vlan_info == 'Remote': if len(conf.split(',')) > 1 or conf.isdigit(): remote_span_vlan = [] if len(conf.split(',')) > 1: remote_span_vlan = conf.split(',') else: remote_span_vlan.append(conf) remote_span = [] for each in remote_span_vlan: remote_span.append(int(each)) config['remote_span'] = remote_span return utils.remove_empties(config)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) match = re.search(r'^(\S+)', conf) intf = match.group(1) if get_interface_type(intf) == 'unknown': return {} if intf.upper()[:2] in ('HU', 'FO', 'TW', 'TE', 'GI', 'FA', 'ET', 'PO'): # populate the facts from the configuration config['name'] = normalize_interface(intf) has_access = utils.parse_conf_arg(conf, 'switchport access vlan') if has_access: config["access"] = {"vlan": int(has_access)} has_voice = utils.parse_conf_arg(conf, 'switchport voice vlan') if has_voice: config["voice"] = {"vlan": int(has_voice)} trunk = dict() trunk["encapsulation"] = utils.parse_conf_arg( conf, 'encapsulation') native_vlan = utils.parse_conf_arg(conf, 'native vlan') if native_vlan: trunk["native_vlan"] = int(native_vlan) allowed_vlan = utils.parse_conf_arg(conf, 'allowed vlan') if allowed_vlan: trunk["allowed_vlans"] = allowed_vlan.split(',') pruning_vlan = utils.parse_conf_arg(conf, 'pruning vlan') if pruning_vlan: trunk['pruning_vlans'] = pruning_vlan.split(',') config['trunk'] = trunk return utils.remove_empties(config)
def _state_merged(self, want, have): """ The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) want_vifs = want_copy.pop('vifs', []) have_vifs = have_copy.pop('vifs', []) updates = dict_diff(have_copy, want_copy) if updates: for key, value in iteritems(updates): commands.append( self._compute_commands(key=key, value=value, interface=want_copy['name'])) if want_vifs: for want_vif in want_vifs: have_vif = search_obj_in_list(want_vif['vlan_id'], have_vifs, key='vlan_id') if not have_vif: have_vif = { 'vlan_id': want_vif['vlan_id'], 'enabled': True } vif_updates = dict_diff(have_vif, want_vif) if vif_updates: for key, value in iteritems(vif_updates): commands.append( self._compute_commands(key=key, value=value, interface=want_copy['name'], vif=want_vif['vlan_id'])) return commands
def set_config(self, existing_lag_interfaces_facts): """ Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params.get('config') if want: for w in want: w.update(remove_empties(w)) if 'members' in w and w['members']: for item in w['members']: item.update( {'member': normalize_interface(item['member'])}) have = existing_lag_interfaces_facts resp = self.set_state(want, have) return to_list(resp)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config['interval'] = conf["openconfig-lldp:config"]["hello-timer"] for item in self.TLV_SELECT_OPTIONS: config["tlv_select"][item.lower()] = ( False if (item in conf["openconfig-lldp:config"] ["suppress-tlv-advertisement"]) else True) return utils.remove_empties(config)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) routes = [] config['address_families'] = [] if conf.get('routing-options'): if conf['routing-options'].get('rib'): if conf['routing-options'].get('rib').get('name') == 'inet6.0': if conf['routing-options'].get('rib').get('static'): route_path = conf['routing-options']['rib'][ 'static'].get('route') routes.append( self._create_route_dict('ipv6', route_path)) if conf['routing-options'].get('static'): route_path = conf['routing-options']['static'].get('route') routes.append(self._create_route_dict('ipv4', route_path)) if conf.get('routing-instances'): config['vrf'] = conf['routing-instances']['instance']['name'] if conf['routing-instances'].get('instance').get( 'routing-options').get('rib').get( 'name') == config['vrf'] + '.inet6.0': if conf['routing-instances']['instance']['routing-options'][ 'rib'].get('static'): route_path = conf['routing-instances']['instance'][ 'routing-options']['rib']['static'].get('route') routes.append(self._create_route_dict('ipv6', route_path)) if conf['routing-instances'].get('instance').get( 'routing-options').get('static'): route_path = conf['routing-instances']['instance'][ 'routing-options']['static'].get('route') routes.append(self._create_route_dict('ipv4', route_path)) config['address_families'].extend(routes) return utils.remove_empties(config)
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) conf = re.split('\n', conf) for command in conf: param = re.search( r'(.*)lldp (\w+(-?)\w+)', command) # get the word after 'lldp' if param: # get the nested-dict/value for that param key2 = re.search(r'%s(.*)' % param.group(2), command) key2 = key2.group(1).strip() key1 = param.group(2).replace('-', '_') if key1 == 'portid_subtype': key1 = 'port_id' config[key1] = key2 elif key1 == 'tlv_select': key2 = key2.split() key2[0] = key2[0].replace('-', '_') if len(key2) == 1: if 'port' in key2[0] or 'system' in key2[0]: # nested dicts key2 = key2[0].split('_') # config[tlv_select][system][name]=False config[key1][key2[0]][key2[1]] = False else: # config[tlv_select][dcbxp]=False config[key1][key2[0]] = False else: # config[tlv_select][management_address][v6]=False config[key1][key2[0]][key2[1]] = False else: config[key1] = key2 # config[reinit]=4 return utils.remove_empties(config)
def populate_facts(self, connection, ansible_facts, data=None): """ Populate the facts for lag_interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected configuration :rtype: dictionary :returns: facts """ if not data: data = connection.get('show running-config | section ^interface') # split the config into instances of the resource resource_delim = 'interface' find_pattern = r'(?:^|\n)%s.*?(?=(?:^|\n)%s|$)' % (resource_delim, resource_delim) resources = [ p.strip() for p in re.findall(find_pattern, data, re.DOTALL) ] objs = {} for resource in resources: if resource: obj = self.render_config(self.generated_spec, resource) if obj: group_name = obj['name'] if group_name in objs and "members" in obj: config = objs[group_name] if "members" not in config: config["members"] = [] objs[group_name]['members'].extend(obj['members']) else: objs[group_name] = obj objs = list(objs.values()) facts = {'lag_interfaces': []} if objs: params = utils.validate_config(self.argument_spec, {'config': objs}) facts['lag_interfaces'] = [ utils.remove_empties(cfg) for cfg in params['config'] ] ansible_facts['ansible_network_resources'].update(facts) return ansible_facts
def _state_merged(want, have): """ The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] if not have: have = {'name': want['name']} for key, value in iteritems( flatten_dict(remove_empties(dict_diff(have, want)))): commands.append(Lacp_interfaces._compute_commands(key, value)) if commands: pad_commands(commands, want['name']) return commands
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) match = re.search(r'^(\S+)', conf) intf = match.group(1) if get_interface_type(intf) == 'unknown': return {} config['name'] = intf config['bfd'] = utils.parse_conf_cmd_arg(conf, 'hsrp bfd', 'enable', 'disable') return utils.remove_empties(config)
def populate_facts(self, connection, ansible_facts, data=None): """ Populate the facts for lacp :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = connection.get("show running-config | include lacp") resources = data.strip() objs = self.render_config(self.generated_spec, resources) ansible_facts['ansible_network_resources'].pop('lacp', None) facts = {} if objs: params = utils.validate_config(self.argument_spec, {'config': objs}) facts['lacp'] = utils.remove_empties(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts
def render_config(self, spec, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config['holdtime'] = utils.parse_conf_arg(conf, 'holdtime') config['reinit'] = utils.parse_conf_arg(conf, 'reinit') config['timer'] = utils.parse_conf_arg(conf, 'timer') for match in re.findall(r'^(no)? lldp tlv-select (\S+)', conf, re.MULTILINE): tlv_option = match[1].replace("-", "_") config['tlv_select'][tlv_option] = bool(match[0] != "no") return utils.remove_empties(config)
def set_config(self, existing_l2_interfaces_facts): """ Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ config = self._module.params.get('config') want = [] if config: for w in config: w.update({'name': normalize_interface(w['name'])}) self.expand_trunk_allowed_vlans(w) want.append(remove_empties(w)) have = existing_l2_interfaces_facts for h in have: self.expand_trunk_allowed_vlans(h) resp = self.set_state(want, have) return to_list(resp)
def set_config(self, existing_vlans_facts): """ Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ config = self._module.params.get('config') want = [] if config: for w in config: if int(w['vlan_id']) == 1: self._module.fail_json( msg="Vlan 1 is not allowed to be managed by this module" ) want.append(remove_empties(w)) have = existing_vlans_facts resp = self.set_state(want, have) return to_list(resp)
def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ arp_monitor_conf = '\n'.join( filter(lambda x: ('arp-monitor' in x), conf)) hash_policy_conf = '\n'.join( filter(lambda x: ('hash-policy' in x), conf)) lag_conf = '\n'.join(filter(lambda x: ('bond' in x), conf)) config = self.parse_attribs(['mode', 'primary'], lag_conf) config['arp_monitor'] = self.parse_arp_monitor(arp_monitor_conf) config['hash_policy'] = self.parse_hash_policy(hash_policy_conf) return utils.remove_empties(config)
def render_config(self, spec, cmd_ref): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ config = deepcopy(spec) config['destination_groups'] = [] config['sensor_groups'] = [] config['subscriptions'] = [] managed_objects = ['TMS_GLOBAL', 'TMS_DESTGROUP', 'TMS_SENSORGROUP', 'TMS_SUBSCRIPTION'] # Walk the argspec and cmd_ref objects and build out config dict. for key in config.keys(): for mo in managed_objects: for cr in cmd_ref[mo]['ref']: cr_keys = cr_key_lookup(key, mo) for cr_key in cr_keys: if cr._ref.get(cr_key) and cr._ref[cr_key].get('existing'): if isinstance(config[key], dict): for k in config[key].keys(): for existing_key in cr._ref[cr_key]['existing'].keys(): config[key][k] = cr._ref[cr_key]['existing'][existing_key][k] continue if isinstance(config[key], list): for existing_key in cr._ref[cr_key]['existing'].keys(): data = get_instance_data(key, cr_key, cr, existing_key) config[key].append(data) continue for existing_key in cr._ref[cr_key]['existing'].keys(): config[key] = cr._ref[cr_key]['existing'][existing_key] elif cr._ref.get(cr_key): data = get_instance_data(key, cr_key, cr, None) if isinstance(config[key], list) and data not in config[key]: config[key].append(data) return utils.remove_empties(config)