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