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