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_interfaces 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.common.utils import to_list 18from ansible.module_utils.network.junos.facts.facts import Facts 19from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring 20from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree 21 22 23class Lldp_interfaces(ConfigBase): 24 """ 25 The junos_lldp_interfaces class 26 """ 27 28 gather_subset = [ 29 '!all', 30 '!min', 31 ] 32 33 gather_network_resources = [ 34 'lldp_interfaces', 35 ] 36 37 def __init__(self, module): 38 super(Lldp_interfaces, self).__init__(module) 39 40 def get_lldp_interfaces_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_interfaces_facts = facts['ansible_network_resources'].get('lldp_interfaces') 47 if not lldp_interfaces_facts: 48 return [] 49 return lldp_interfaces_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 warnings = list() 58 59 existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() 60 config_xmls = self.set_config(existing_lldp_interfaces_facts) 61 62 with locked_config(self._module): 63 for config_xml in to_list(config_xmls): 64 diff = load_config(self._module, config_xml, warnings) 65 66 commit = not self._module.check_mode 67 if diff: 68 if commit: 69 commit_configuration(self._module) 70 else: 71 discard_changes(self._module) 72 result['changed'] = True 73 74 if self._module._diff: 75 result['diff'] = {'prepared': diff} 76 77 result['commands'] = config_xmls 78 79 changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() 80 81 result['before'] = existing_lldp_interfaces_facts 82 if result['changed']: 83 result['after'] = changed_lldp_interfaces_facts 84 85 return result 86 87 def set_config(self, existing_lldp_interfaces_facts): 88 """ Collect the configuration from the args passed to the module, 89 collect the current configuration (as a dict from facts) 90 :rtype: A list 91 :returns: the commands necessary to migrate the current configuration 92 to the desired configuration 93 """ 94 want = self._module.params['config'] 95 have = existing_lldp_interfaces_facts 96 resp = self.set_state(want, have) 97 return to_list(resp) 98 99 def set_state(self, want, have): 100 """ Select the appropriate function based on the state provided 101 :param want: the desired configuration as a dictionary 102 :param have: the current configuration as a dictionary 103 :rtype: A list 104 :returns: the commands necessary to migrate the current configuration 105 to the desired configuration 106 """ 107 root = build_root_xml_node('protocols') 108 lldp_intf_ele = build_subtree(root, 'lldp') 109 110 state = self._module.params['state'] 111 if state == 'overridden': 112 config_xmls = self._state_overridden(want, have) 113 elif state == 'deleted': 114 config_xmls = self._state_deleted(want, have) 115 elif state == 'merged': 116 config_xmls = self._state_merged(want, have) 117 elif state == 'replaced': 118 config_xmls = self._state_replaced(want, have) 119 120 for xml in config_xmls: 121 lldp_intf_ele.append(xml) 122 123 return tostring(root) 124 125 def _state_replaced(self, want, have): 126 """ The xml configuration generator when state is replaced 127 :rtype: A list 128 :returns: the xml configuration necessary to migrate the current configuration 129 to the desired configuration 130 """ 131 lldp_intf_xml = [] 132 lldp_intf_xml.extend(self._state_deleted(want, have)) 133 lldp_intf_xml.extend(self._state_merged(want, have)) 134 135 return lldp_intf_xml 136 137 def _state_overridden(self, want, have): 138 """ The xml configuration generator when state is overridden 139 :rtype: A list 140 :returns: the xml configuration necessary to migrate the current configuration 141 to the desired configuration 142 """ 143 lldp_intf_xmls_obj = [] 144 145 # replace interface config with data in want 146 lldp_intf_xmls_obj.extend(self._state_replaced(want, have)) 147 148 # delete interface config if interface in have not present in want 149 delete_obj = [] 150 for have_obj in have: 151 for want_obj in want: 152 if have_obj['name'] == want_obj['name']: 153 break 154 else: 155 delete_obj.append(have_obj) 156 157 if len(delete_obj): 158 lldp_intf_xmls_obj.extend(self._state_deleted(delete_obj, have)) 159 160 return lldp_intf_xmls_obj 161 162 def _state_merged(self, want, have): 163 """ The xml configuration generator when state is merged 164 :rtype: A list 165 :returns: the xml configuration necessary to merge the provided into 166 the current configuration 167 """ 168 lldp_intf_xml = [] 169 for config in want: 170 lldp_intf_root = build_root_xml_node('interface') 171 172 if config.get('name'): 173 build_child_xml_node(lldp_intf_root, 'name', config['name']) 174 175 if config.get('enabled') is not None: 176 if config['enabled'] is False: 177 build_child_xml_node(lldp_intf_root, 'disable') 178 else: 179 build_child_xml_node(lldp_intf_root, 'disable', None, {'delete': 'delete'}) 180 else: 181 build_child_xml_node(lldp_intf_root, 'disable', None, {'delete': 'delete'}) 182 lldp_intf_xml.append(lldp_intf_root) 183 return lldp_intf_xml 184 185 def _state_deleted(self, want, have): 186 """ The xml configuration generator when state is deleted 187 :rtype: A list 188 :returns: the xml configuration necessary to remove the current configuration 189 of the provided objects 190 """ 191 lldp_intf_xml = [] 192 intf_obj = want 193 194 if not intf_obj: 195 # delete lldp interfaces attribute from all the existing interface 196 intf_obj = have 197 198 for config in intf_obj: 199 lldp_intf_root = build_root_xml_node('interface') 200 lldp_intf_root.attrib.update({'delete': 'delete'}) 201 build_child_xml_node(lldp_intf_root, 'name', config['name']) 202 203 lldp_intf_xml.append(lldp_intf_root) 204 205 return lldp_intf_xml 206