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 ios_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 its desired end-state is 11created 12""" 13 14from __future__ import absolute_import, division, print_function 15__metaclass__ = type 16 17 18from ansible.module_utils.network.common.cfg.base import ConfigBase 19from ansible.module_utils.network.common.utils import to_list 20from ansible.module_utils.network.ios.facts.facts import Facts 21from ansible.module_utils.network.ios.utils.utils import dict_to_set 22from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list 23from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface 24 25 26class Lldp_Interfaces(ConfigBase): 27 """ 28 The ios_lldp_interfaces class 29 """ 30 31 gather_subset = [ 32 '!all', 33 '!min', 34 ] 35 36 gather_network_resources = [ 37 'lldp_interfaces', 38 ] 39 40 def __init__(self, module): 41 super(Lldp_Interfaces, self).__init__(module) 42 43 def get_lldp_interfaces_facts(self): 44 """ Get the 'facts' (the current configuration) 45 46 :rtype: A dictionary 47 :returns: The current configuration as a dictionary 48 """ 49 facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) 50 lldp_interfaces_facts = facts['ansible_network_resources'].get('lldp_interfaces') 51 52 if not lldp_interfaces_facts: 53 return [] 54 return lldp_interfaces_facts 55 56 def execute_module(self): 57 """ Execute the module 58 59 :rtype: A dictionary 60 :returns: The result from module execution 61 """ 62 result = {'changed': False} 63 commands = list() 64 warnings = list() 65 66 existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() 67 commands.extend(self.set_config(existing_lldp_interfaces_facts)) 68 if commands: 69 if not self._module.check_mode: 70 self._connection.edit_config(commands) 71 result['changed'] = True 72 result['commands'] = commands 73 74 changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() 75 76 result['before'] = existing_lldp_interfaces_facts 77 if result['changed']: 78 result['after'] = changed_lldp_interfaces_facts 79 80 result['warnings'] = warnings 81 82 return result 83 84 def set_config(self, existing_lldp_interfaces_facts): 85 """ Collect the configuration from the args passed to the module, 86 collect the current configuration (as a dict from facts) 87 88 :rtype: A list 89 :returns: the commands necessary to migrate the current configuration 90 to the desired configuration 91 """ 92 want = self._module.params['config'] 93 have = existing_lldp_interfaces_facts 94 resp = self.set_state(want, have) 95 96 return to_list(resp) 97 98 def set_state(self, want, have): 99 """ Select the appropriate function based on the state provided 100 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 state = self._module.params['state'] 108 if state in ('overridden', 'merged', 'replaced') and not want: 109 self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) 110 111 if state == 'overridden': 112 commands = self._state_overridden(want, have) 113 elif state == 'deleted': 114 commands = self._state_deleted(want, have) 115 elif state == 'merged': 116 commands = self._state_merged(want, have) 117 elif state == 'replaced': 118 commands = self._state_replaced(want, have) 119 120 return commands 121 122 def _state_replaced(self, want, have): 123 """ The command generator when state is replaced 124 125 :rtype: A list 126 :returns: the commands necessary to migrate the current configuration 127 to the desired configuration 128 """ 129 commands = [] 130 131 for interface in want: 132 for each in have: 133 if each['name'] == interface['name']: 134 break 135 else: 136 continue 137 have_dict = filter_dict_having_none_value(interface, each) 138 commands.extend(self._clear_config(dict(), have_dict)) 139 commands.extend(self._set_config(interface, each)) 140 # Remove the duplicate interface call 141 commands = remove_duplicate_interface(commands) 142 143 return commands 144 145 def _state_overridden(self, want, have): 146 """ The command generator when state is overridden 147 148 :rtype: A list 149 :returns: the commands necessary to migrate the current configuration 150 to the desired configuration 151 """ 152 commands = [] 153 154 for each in have: 155 for interface in want: 156 if each['name'] == interface['name']: 157 break 158 else: 159 # We didn't find a matching desired state, which means we can 160 # pretend we recieved an empty desired state. 161 interface = dict(name=each['name']) 162 commands.extend(self._clear_config(interface, each)) 163 continue 164 have_dict = filter_dict_having_none_value(interface, each) 165 commands.extend(self._clear_config(dict(), have_dict)) 166 commands.extend(self._set_config(interface, each)) 167 # Remove the duplicate interface call 168 commands = remove_duplicate_interface(commands) 169 170 return commands 171 172 def _state_merged(self, want, have): 173 """ The command generator when state is merged 174 175 :rtype: A list 176 :returns: the commands necessary to merge the provided into 177 the current configuration 178 """ 179 commands = [] 180 181 for interface in want: 182 for each in have: 183 if interface['name'] == each['name']: 184 break 185 else: 186 continue 187 commands.extend(self._set_config(interface, each)) 188 189 return commands 190 191 def _state_deleted(self, want, have): 192 """ The command generator when state is deleted 193 194 :rtype: A list 195 :returns: the commands necessary to remove the current configuration 196 of the provided objects 197 """ 198 commands = [] 199 200 if want: 201 for interface in want: 202 for each in have: 203 if each['name'] == interface['name']: 204 break 205 else: 206 continue 207 interface = dict(name=interface['name']) 208 commands.extend(self._clear_config(interface, each)) 209 else: 210 for each in have: 211 commands.extend(self._clear_config(dict(), each)) 212 213 return commands 214 215 def _set_config(self, want, have): 216 # Set the interface config based on the want and have config 217 commands = [] 218 219 interface = 'interface ' + have['name'] 220 # Get the diff b/w want and have 221 want_dict = dict_to_set(want) 222 have_dict = dict_to_set(have) 223 diff = want_dict - have_dict 224 225 if diff: 226 diff = dict(diff) 227 receive = diff.get('receive') 228 transmit = diff.get('transmit') 229 med_tlv_select = diff.get('med_tlv_select') 230 tlv_select = diff.get('tlv_select') 231 if receive: 232 cmd = 'lldp receive' 233 add_command_to_config_list(interface, cmd, commands) 234 elif receive is False: 235 cmd = 'no lldp receive' 236 add_command_to_config_list(interface, cmd, commands) 237 if transmit: 238 cmd = 'lldp transmit' 239 add_command_to_config_list(interface, cmd, commands) 240 elif transmit is False: 241 cmd = 'no lldp transmit' 242 add_command_to_config_list(interface, cmd, commands) 243 244 if med_tlv_select: 245 med_tlv_select = dict(med_tlv_select) 246 if med_tlv_select.get('inventory_management'): 247 add_command_to_config_list(interface, 'lldp med-tlv-select inventory-management', commands) 248 if tlv_select: 249 tlv_select = dict(tlv_select) 250 if tlv_select.get('power_management'): 251 add_command_to_config_list(interface, 'lldp tlv-select power-management', commands) 252 253 return commands 254 255 def _clear_config(self, want, have): 256 # Delete the interface config based on the want and have config 257 commands = [] 258 if want.get('name'): 259 interface = 'interface ' + want['name'] 260 else: 261 interface = 'interface ' + have['name'] 262 263 if have.get('receive') and have.get('receive') != want.get('receive'): 264 cmd = 'lldp receive' 265 remove_command_from_config_list(interface, cmd, commands) 266 if have.get('transmit') and have.get('transmit') != want.get('transmit'): 267 cmd = 'lldp transmit' 268 remove_command_from_config_list(interface, cmd, commands) 269 270 return commands 271