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 junos_lldp class
8It is in this file where the current configuration (as dict)
9is compared to the provided configuration (as dict) and the command set
10necessary to bring the current configuration to it's desired end-state is
11created
12"""
13from __future__ import absolute_import, division, print_function
14__metaclass__ = type
15
16from ansible.module_utils.network.common.cfg.base import ConfigBase
17from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
18from ansible.module_utils.network.common.utils import to_list
19from ansible.module_utils.network.junos.facts.facts import Facts
20from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node
21
22
23class Lldp_global(ConfigBase):
24    """
25    The junos_lldp class
26    """
27
28    gather_subset = [
29        '!all',
30        '!min',
31    ]
32
33    gather_network_resources = [
34        'lldp_global',
35    ]
36
37    def __init__(self, module):
38        super(Lldp_global, self).__init__(module)
39
40    def get_lldp_global_facts(self):
41        """ Get the 'facts' (the current configuration)
42        :rtype: A dictionary
43        :returns: The current configuration as a dictionary
44        """
45        facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
46        lldp_facts = facts['ansible_network_resources'].get('lldp_global')
47        if not lldp_facts:
48            return {}
49        return lldp_facts
50
51    def execute_module(self):
52        """ Execute the module
53        :rtype: A dictionary
54        :returns: The result from module execution
55        """
56        result = {'changed': False}
57
58        existing_lldp_global_facts = self.get_lldp_global_facts()
59        config_xmls = self.set_config(existing_lldp_global_facts)
60
61        with locked_config(self._module):
62            for config_xml in to_list(config_xmls):
63                diff = load_config(self._module, config_xml, [])
64
65            commit = not self._module.check_mode
66            if diff:
67                if commit:
68                    commit_configuration(self._module)
69                else:
70                    discard_changes(self._module)
71                result['changed'] = True
72
73                if self._module._diff:
74                    result['diff'] = {'prepared': diff}
75
76        result['commands'] = config_xmls
77
78        changed_lldp_global_facts = self.get_lldp_global_facts()
79
80        result['before'] = existing_lldp_global_facts
81        if result['changed']:
82            result['after'] = changed_lldp_global_facts
83
84        return result
85
86    def set_config(self, existing_lldp_global_facts):
87        """ Collect the configuration from the args passed to the module,
88            collect the current configuration (as a dict from facts)
89        :rtype: A list
90        :returns: the commands necessary to migrate the current configuration
91                  to the desired configuration
92        """
93        want = self._module.params['config']
94        have = existing_lldp_global_facts
95        resp = self.set_state(want, have)
96        return to_list(resp)
97
98    def set_state(self, want, have):
99        """ Select the appropriate function based on the state provided
100        :param want: the desired configuration as a dictionary
101        :param have: the current configuration as a dictionary
102        :rtype: A list
103        :returns: the list xml configuration necessary to migrate the current configuration
104                  to the desired configuration
105        """
106        root = build_root_xml_node('protocols')
107        state = self._module.params['state']
108        if state == 'deleted':
109            config_xmls = self._state_deleted(want, have)
110        elif state == 'merged':
111            config_xmls = self._state_merged(want, have)
112        elif state == 'replaced':
113            config_xmls = self._state_replaced(want, have)
114
115        for xml in config_xmls:
116            root.append(xml)
117        return tostring(root)
118
119    def _state_replaced(self, want, have):
120        """ The xml configuration generator when state is merged
121         :rtype: A list
122         :returns: the xml configuration necessary to merge the provided into
123                   the current configuration
124         """
125        lldp_xml = []
126        lldp_xml.extend(self._state_deleted(want, have))
127        lldp_xml.extend(self._state_merged(want, have))
128
129        return lldp_xml
130
131    def _state_merged(self, want, have):
132        """ Select the appropriate function based on the state provided
133        :param want: the desired configuration as a dictionary
134        :param have: the current configuration as a dictionary
135        :rtype: A list
136        :returns: the list xml configuration necessary to migrate the current configuration
137                  to the desired configuration
138        """
139        lldp_xml = []
140
141        lldp_root = build_root_xml_node('lldp')
142        if want.get('address'):
143            build_child_xml_node(lldp_root, 'management-address', want['address'])
144        if want.get('interval'):
145            build_child_xml_node(lldp_root, 'advertisement-interval', want['interval'])
146        if want.get('transmit_delay'):
147            build_child_xml_node(lldp_root, 'transmit-delay', want['transmit_delay'])
148        if want.get('hold_multiplier'):
149            build_child_xml_node(lldp_root, 'hold-multiplier', want['hold_multiplier'])
150        enable = want.get('enable')
151        if enable is not None:
152            if enable is False:
153                build_child_xml_node(lldp_root, 'disable')
154            else:
155                build_child_xml_node(lldp_root, 'disable', None, {'delete': 'delete'})
156        else:
157            build_child_xml_node(lldp_root, 'disable', None, {'delete': 'delete'})
158        lldp_xml.append(lldp_root)
159
160        return lldp_xml
161
162    def _state_deleted(self, want, have):
163        """ The command generator when state is deleted
164        :rtype: A list
165        :returns: the commands necessary to remove the current configuration
166                  of the provided objects
167        """
168        lldp_xml = []
169
170        lldp_root = build_root_xml_node('lldp')
171        build_child_xml_node(lldp_root, 'management-address', None, {'delete': 'delete'})
172        build_child_xml_node(lldp_root, 'advertisement-interval', None, {'delete': 'delete'})
173        build_child_xml_node(lldp_root, 'transmit-delay', None, {'delete': 'delete'})
174        build_child_xml_node(lldp_root, 'hold-multiplier', None, {'delete': 'delete'})
175        build_child_xml_node(lldp_root, 'disable', None, {'delete': 'delete'})
176        lldp_xml.append(lldp_root)
177        return lldp_xml
178