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