1# -*- coding: utf-8 -*- 2# Copyright 2019 Red Hat 3# GNU General Public License v3.0+ 4# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5""" 6The eos_lag_interfaces class 7It is in this file where the current configuration (as dict) 8is compared to the provided configuration (as dict) and the command set 9necessary to bring the current configuration to it's desired end-state is 10created 11""" 12 13from __future__ import (absolute_import, division, print_function) 14__metaclass__ = type 15 16from ansible.module_utils.network.common.utils import to_list, dict_diff 17 18from ansible.module_utils.network.common.cfg.base import ConfigBase 19from ansible.module_utils.network.eos.facts.facts import Facts 20from ansible.module_utils.network.eos.utils.utils import normalize_interface 21 22 23class Lag_interfaces(ConfigBase): 24 """ 25 The eos_lag_interfaces class 26 """ 27 28 gather_subset = [ 29 '!all', 30 '!min', 31 ] 32 33 gather_network_resources = [ 34 'lag_interfaces', 35 ] 36 37 def get_lag_interfaces_facts(self): 38 """ Get the 'facts' (the current configuration) 39 40 :rtype: A dictionary 41 :returns: The current configuration as a dictionary 42 """ 43 facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) 44 lag_interfaces_facts = facts['ansible_network_resources'].get('lag_interfaces') 45 if not lag_interfaces_facts: 46 return [] 47 return lag_interfaces_facts 48 49 def execute_module(self): 50 """ Execute the module 51 52 :rtype: A dictionary 53 :returns: The result from module execution 54 """ 55 result = {'changed': False} 56 commands = list() 57 warnings = list() 58 59 existing_lag_interfaces_facts = self.get_lag_interfaces_facts() 60 commands.extend(self.set_config(existing_lag_interfaces_facts)) 61 if commands: 62 if not self._module.check_mode: 63 self._connection.edit_config(commands) 64 result['changed'] = True 65 result['commands'] = commands 66 67 changed_lag_interfaces_facts = self.get_lag_interfaces_facts() 68 69 result['before'] = existing_lag_interfaces_facts 70 if result['changed']: 71 result['after'] = changed_lag_interfaces_facts 72 73 result['warnings'] = warnings 74 return result 75 76 def set_config(self, existing_lag_interfaces_facts): 77 """ Collect the configuration from the args passed to the module, 78 collect the current configuration (as a dict from facts) 79 80 :rtype: A list 81 :returns: the commands necessary to migrate the current configuration 82 to the desired configuration 83 """ 84 want = self._module.params['config'] 85 have = existing_lag_interfaces_facts 86 resp = self.set_state(want, have) 87 return to_list(resp) 88 89 def set_state(self, want, have): 90 """ Select the appropriate function based on the state provided 91 92 :param want: the desired configuration as a dictionary 93 :param have: the current configuration as a dictionary 94 :rtype: A list 95 :returns: the commands necessary to migrate the current configuration 96 to the desired configuration 97 """ 98 state = self._module.params['state'] 99 if state == 'overridden': 100 commands = self._state_overridden(want, have) 101 elif state == 'deleted': 102 commands = self._state_deleted(want, have) 103 elif state == 'merged': 104 commands = self._state_merged(want, have) 105 elif state == 'replaced': 106 commands = self._state_replaced(want, have) 107 return commands 108 109 @staticmethod 110 def _state_replaced(want, have): 111 """ The command generator when state is replaced 112 :rtype: A list 113 :returns: the commands necessary to migrate the current configuration 114 to the desired configuration 115 """ 116 commands = [] 117 for interface in want: 118 interface_name = normalize_interface(interface["name"]) 119 for extant in have: 120 if extant["name"] == interface_name: 121 break 122 else: 123 extant = dict(name=interface_name) 124 125 commands.extend(set_config(interface, extant)) 126 commands.extend(remove_config(interface, extant)) 127 128 return commands 129 130 @staticmethod 131 def _state_overridden(want, have): 132 """ The command generator when state is overridden 133 :rtype: A list 134 :returns: the commands necessary to migrate the current configuration 135 to the desired configuration 136 """ 137 commands = [] 138 for extant in have: 139 for interface in want: 140 if normalize_interface(interface["name"]) == extant["name"]: 141 break 142 else: 143 interface = dict(name=extant["name"]) 144 commands.extend(remove_config(interface, extant)) 145 146 for interface in want: 147 interface_name = normalize_interface(interface["name"]) 148 for extant in have: 149 if extant["name"] == interface_name: 150 break 151 else: 152 extant = dict(name=interface_name) 153 commands.extend(set_config(interface, extant)) 154 155 return commands 156 157 @staticmethod 158 def _state_merged(want, have): 159 """ The command generator when state is merged 160 :rtype: A list 161 :returns: the commands necessary to merge the provided into 162 the current configuration 163 """ 164 commands = [] 165 for interface in want: 166 interface_name = normalize_interface(interface["name"]) 167 for extant in have: 168 if extant["name"] == interface_name: 169 break 170 else: 171 extant = dict(name=interface_name) 172 173 commands.extend(set_config(interface, extant)) 174 175 return commands 176 177 @staticmethod 178 def _state_deleted(want, have): 179 """ The command generator when state is deleted 180 :rtype: A list 181 :returns: the commands necessary to remove the current configuration 182 of the provided objects 183 """ 184 commands = [] 185 for interface in want: 186 interface_name = normalize_interface(interface["name"]) 187 for extant in have: 188 if extant["name"] == interface_name: 189 break 190 else: 191 extant = dict(name=interface_name) 192 193 # Clearing all args, send empty dictionary 194 interface = dict(name=interface_name) 195 commands.extend(remove_config(interface, extant)) 196 197 return commands 198 199 200def set_config(want, have): 201 commands = [] 202 to_set = dict_diff(have, want) 203 for member in to_set.get("members", []): 204 channel_id = want["name"][12:] 205 commands.extend([ 206 "interface {0}".format(member["member"]), 207 "channel-group {0} mode {1}".format(channel_id, member["mode"]), 208 ]) 209 210 return commands 211 212 213def remove_config(want, have): 214 commands = [] 215 if not want.get("members"): 216 return ["no interface {0}".format(want["name"])] 217 218 to_remove = dict_diff(want, have) 219 for member in to_remove.get("members", []): 220 commands.extend([ 221 "interface {0}".format(member["member"]), 222 "no channel-group", 223 ]) 224 225 return commands 226