1# 2# -*- coding: utf-8 -*- 3# Copyright 2019 Red Hat 4# GNU General Public License v3.0+ 5# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6""" 7The vyos l3_interfaces fact class 8It is in this file the configuration is collected from the device 9for a given resource, parsed, and the facts tree is populated 10based on the configuration. 11""" 12 13from __future__ import absolute_import, division, print_function 14__metaclass__ = type 15 16 17import re 18from copy import deepcopy 19from ansible.module_utils.network.common import utils 20from ansible.module_utils.six import iteritems 21from ansible.module_utils.compat import ipaddress 22from ansible.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs 23 24 25class L3_interfacesFacts(object): 26 """ The vyos l3_interfaces fact class 27 """ 28 29 def __init__(self, module, subspec='config', options='options'): 30 self._module = module 31 self.argument_spec = L3_interfacesArgs.argument_spec 32 spec = deepcopy(self.argument_spec) 33 if subspec: 34 if options: 35 facts_argument_spec = spec[subspec][options] 36 else: 37 facts_argument_spec = spec[subspec] 38 else: 39 facts_argument_spec = spec 40 41 self.generated_spec = utils.generate_dict(facts_argument_spec) 42 43 def populate_facts(self, connection, ansible_facts, data=None): 44 """ Populate the facts for l3_interfaces 45 :param connection: the device connection 46 :param ansible_facts: Facts dictionary 47 :param data: previously collected conf 48 :rtype: dictionary 49 :returns: facts 50 """ 51 if not data: 52 data = connection.get_config() 53 54 # operate on a collection of resource x 55 objs = [] 56 interface_names = re.findall(r'set interfaces (?:ethernet|bonding|vti|vxlan) (?:\'*)(\S+)(?:\'*)', data, re.M) 57 if interface_names: 58 for interface in set(interface_names): 59 intf_regex = r' %s .+$' % interface 60 cfg = re.findall(intf_regex, data, re.M) 61 obj = self.render_config(cfg) 62 obj['name'] = interface.strip("'") 63 if obj: 64 objs.append(obj) 65 66 ansible_facts['ansible_network_resources'].pop('l3_interfaces', None) 67 facts = {} 68 if objs: 69 facts['l3_interfaces'] = [] 70 params = utils.validate_config(self.argument_spec, {'config': objs}) 71 for cfg in params['config']: 72 facts['l3_interfaces'].append(utils.remove_empties(cfg)) 73 74 ansible_facts['ansible_network_resources'].update(facts) 75 return ansible_facts 76 77 def render_config(self, conf): 78 """ 79 Render config as dictionary structure and delete keys from spec for null values 80 :param spec: The facts tree, generated from the argspec 81 :param conf: The configuration 82 :rtype: dictionary 83 :returns: The generated config 84 """ 85 vif_conf = '\n'.join(filter(lambda x: ('vif' in x), conf)) 86 eth_conf = '\n'.join(filter(lambda x: ('vif' not in x), conf)) 87 config = self.parse_attribs(eth_conf) 88 config['vifs'] = self.parse_vifs(vif_conf) 89 90 return utils.remove_empties(config) 91 92 def parse_vifs(self, conf): 93 vif_names = re.findall(r'vif (\d+)', conf, re.M) 94 vifs_list = None 95 if vif_names: 96 vifs_list = [] 97 for vif in set(vif_names): 98 vif_regex = r' %s .+$' % vif 99 cfg = '\n'.join(re.findall(vif_regex, conf, re.M)) 100 obj = self.parse_attribs(cfg) 101 obj['vlan_id'] = vif 102 if obj: 103 vifs_list.append(obj) 104 105 return vifs_list 106 107 def parse_attribs(self, conf): 108 config = {} 109 ipaddrs = re.findall(r'address (\S+)', conf, re.M) 110 config['ipv4'] = [] 111 config['ipv6'] = [] 112 113 for item in ipaddrs: 114 item = item.strip("'") 115 if item == 'dhcp': 116 config['ipv4'].append({'address': item}) 117 elif item == 'dhcpv6': 118 config['ipv6'].append({'address': item}) 119 else: 120 ip_version = ipaddress.ip_address(item.split("/")[0]).version 121 if ip_version == 4: 122 config['ipv4'].append({'address': item}) 123 else: 124 config['ipv6'].append({'address': item}) 125 126 for key, value in iteritems(config): 127 if value == []: 128 config[key] = None 129 130 return utils.remove_empties(config) 131