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 iosxr_ospfv2 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 15__metaclass__ = type 16 17from copy import deepcopy 18from ansible.module_utils.six import iteritems 19from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.facts.facts import ( 20 Facts, 21) 22 23from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.rm_templates.ospfv2 import ( 24 Ospfv2Template, 25) 26from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import ( 27 ResourceModule, 28) 29from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( 30 dict_merge, 31) 32 33 34class Ospfv2(ResourceModule): 35 """ 36 The ios_ospfv2 class 37 """ 38 39 gather_subset = ["!all", "!min"] 40 41 gather_network_resources = ["ospfv2"] 42 43 def __init__(self, module): 44 super(Ospfv2, self).__init__( 45 empty_fact_val={}, 46 facts_module=Facts(module), 47 module=module, 48 resource="ospfv2", 49 tmplt=Ospfv2Template(), 50 ) 51 52 def execute_module(self): 53 """ Execute the module 54 :rtype: A dictionary 55 :returns: The result from module execution 56 """ 57 self.gen_config() 58 self.run_commands() 59 60 return self.result 61 62 def gen_config(self): 63 """ Select the appropriate function based on the state provided 64 :rtype: A list 65 :returns: the commands necessary to migrate the current configuration 66 to the desired configuration 67 """ 68 if self.want: 69 wantd = { 70 (entry["process_id"], entry.get("vrf")): entry 71 for entry in self.want.get("processes", []) 72 } 73 else: 74 wantd = {} 75 if self.have: 76 haved = { 77 (entry["process_id"], entry.get("vrf")): entry 78 for entry in self.have.get("processes", []) 79 } 80 else: 81 haved = {} 82 83 # turn all lists of dicts into dicts prior to merge 84 for thing in wantd, haved: 85 for _pid, proc in iteritems(thing): 86 for area in proc.get("areas", []): 87 virtual_link = { 88 entry["id"]: entry 89 for entry in area.get("virtual_link", []) 90 } 91 if bool(virtual_link): 92 area["virtual_link"] = virtual_link 93 ranges = { 94 entry["address"]: entry 95 for entry in area.get("ranges", []) 96 } 97 if bool(ranges): 98 area["ranges"] = ranges 99 100 proc["areas"] = { 101 entry["area_id"]: entry for entry in proc.get("areas", []) 102 } 103 if proc.get("distribute_list"): 104 if "acls" in proc.get("distribute_list"): 105 proc["distribute_list"]["acls"] = { 106 entry["name"]: entry 107 for entry in proc["distribute_list"].get( 108 "acls", [] 109 ) 110 } 111 112 # if state is merged, merge want onto have 113 if self.state == "merged": 114 wantd = dict_merge(haved, wantd) 115 116 # if state is deleted, limit the have to anything in want 117 # set want to nothing 118 if self.state == "deleted": 119 haved = { 120 k: v for k, v in iteritems(haved) if k in wantd or not wantd 121 } 122 wantd = {} 123 124 # delete processes first so we do run into "more than one" errors 125 if self.state == "deleted": 126 haved_del = deepcopy(haved) 127 want_process = {} 128 for k, t_want in iteritems(haved_del): 129 want_process["process_id"] = t_want.get("process_id") 130 if not (len(t_want) == 2 and not t_want.get("areas")): 131 self._compare(want=want_process, have=haved_del.get(k, {})) 132 if self.state == "overridden": 133 haved_del = deepcopy(haved) 134 want = {} 135 for k, t_want in iteritems(haved_del): 136 if k not in wantd: 137 want["process_id"] = t_want.get("process_id") 138 if not (len(t_want) == 2 and not t_want.get("areas")): 139 self._compare(want=want, have=haved_del.get(k, {})) 140 141 for k, want in iteritems(wantd): 142 self._compare(want=want, have=haved.pop(k, {})) 143 144 def _compare(self, want, have): 145 parsers = [ 146 "bfd", 147 "cost", 148 "weight", 149 "passive", 150 "priority", 151 "protocol", 152 "auto_cost", 153 "bandwidth", 154 "flood_reduction", 155 "default_metric", 156 "default_weight", 157 "router_id", 158 "demand_circuit", 159 "packet_size", 160 "transmit_delay", 161 "summary_in", 162 "external_out", 163 "dead_interval", 164 "hello_interval", 165 "authentication", 166 "adjacency_stagger", 167 "retransmit_interval", 168 "mtu_ignore", 169 "bfd.fast_detect", 170 "capability", 171 "capability.opaque", 172 "admin_distance", 173 "ospf_distance", 174 "address_family_unicast", 175 "loopback_stub_network", 176 "authentication.message_digest", 177 "default_information_originate", 178 "link_down_fast_detect", 179 "nsr", 180 "database_filter", 181 "log_adjacency", 182 "distribute_bgp_ls", 183 "distribute_link_state", 184 "max_lsa", 185 "max_metric", 186 "mpls_ldp", 187 "mpls_traffic_eng", 188 "microloop_avoidance", 189 "prefix_suppression", 190 "protocol_shutdown", 191 "timers.lsa", 192 "timers.graceful_shutdown", 193 "throttle.lsa_all", 194 "throttle.spf", 195 "throttle.fast_reroute", 196 "timers.pacing_flood", 197 ] 198 199 if want != have: 200 self.addcmd(want or have, "pid", False) 201 self.compare(parsers, want, have) 202 self._areas_compare(want, have) 203 204 def _areas_compare(self, want, have): 205 wareas = want.get("areas", {}) 206 hareas = have.get("areas", {}) 207 for name, entry in iteritems(wareas): 208 self._area_compare(want=entry, have=hareas.pop(name, {})) 209 for name, entry in iteritems(hareas): 210 self._area_compare(want={}, have=entry) 211 212 def _area_compare(self, want, have): 213 parsers = [ 214 "area.authentication", 215 "area.authentication_key", 216 "area.authentication.message_digest", 217 "area.mpls_traffic_eng", 218 "area.mpls_ldp", 219 "area.bfd", 220 "area.bfd.fast_detect", 221 "area.nssa", 222 "area.nssa.default_information_originate", 223 "area.nssa.translate", 224 "area.default_cost", 225 "area.stub", 226 "area.ranges", 227 "area.cost", 228 "area.dead_interval", 229 "area.hello_interval", 230 "area.transmit_delay", 231 "area.mtu_ignore", 232 "area.packet_size", 233 "area.priority", 234 "area.weight", 235 "area.external_out", 236 "area.summary_in", 237 "area.demand_circuit", 238 "area.passive", 239 ] 240 self.compare(parsers=parsers, want=want, have=have) 241 self._areas_compare_virtual_link(want, have) 242 243 def _areas_compare_virtual_link(self, want, have): 244 wvlinks = want.get("virtual_link", {}) 245 hvlinks = have.get("virtual_link", {}) 246 for name, entry in iteritems(wvlinks): 247 self._area_compare_virtual_link( 248 want=entry, have=hvlinks.pop(name, {}) 249 ) 250 for name, entry in iteritems(hvlinks): 251 self._area_compare_virtual_link(want={}, have=entry) 252 253 def _area_compare_virtual_link(self, want, have): 254 parsers = [ 255 "virtual_link.authentication", 256 "virtual_link.authentication_key", 257 "virtual_link.authentication.message_digest", 258 "virtual_link.hello_interval", 259 "virtual_link.dead_interval", 260 "virtual_link.retransmit_interval", 261 ] 262 self.compare(parsers=parsers, want=want, have=have) 263