def build_layer3_graph(related_extra=None): """Build a graph representation of the layer 3 topology stored in the NAV database. :param related_extra Additional selection_related fields : returns: A MultiDiGraph of Netbox nodes, edges annotated with Interface model objects. """ graph = nx.MultiGraph(name="Layer 3 topology") select_related = ('interface__netbox', 'interface__to_netbox', 'interface__to_interface', 'interface__to_interface__netbox') if related_extra: select_related = select_related + related_extra prefixes = Prefix.objects.filter( vlan__net_type__in=('link', 'elink', 'core')).extra(where=[ 'NOT (family(netaddr) = 4 AND masklen(netaddr) = 32)', 'NOT (family(netaddr) = 6 AND masklen(netaddr) = 128)' ]).select_related("vlan__net_type") router_ports = GwPortPrefix.objects.filter( prefix__in=prefixes, interface__netbox__category__in=('GW', 'GSW')).select_related( *select_related) router_ports_prefix_map = defaultdict(list) for router_port in router_ports: router_ports_prefix_map[router_port.prefix].append(router_port) def _add_edge(gwportprefixes_in_prefix): """ Adds connections between netboxes in gwportprefix (fully connected network) note: loop/self.loop edges should _NOT_ use this method for adding the loop to the graph. """ for this in gwportprefixes_in_prefix: for gwpp in gwportprefixes_in_prefix: if this is not gwpp: graph.add_edge(this, gwpp, key=this.prefix) for prefix in prefixes: gwportprefixes = router_ports_prefix_map.get(prefix) if gwportprefixes: if prefix.vlan.net_type.id == 'elink': if len(gwportprefixes) > 1: # Special case, (horrible) check if it's a local loopback # to same netbox. # # d3js force directed doesn't show loopback edges, # but we'll include it in the graph metadata, in case we # fix the visualizing later. # take first GwPortPrefix in list of GwPortPrefixes, # and use as base to check for loop back edges linking to # the same netbox. gwpp_match = gwportprefixes[0] if ([ u.interface.netbox == gwpp_match.interface.netbox for u in gwportprefixes ].count(True) >= 2): for u in gwportprefixes: for v in gwportprefixes: if u is not v: graph.add_edge(u, v, key=prefix) else: # If not, we'll add the edge anyway and log a warning # about topology detector should really not classify # this as an elink. (since we found >1 known gwpp's # in given prefix means it shold be a link or core.) _LOGGER.warning( "Topology error? %s classified as elink, " "we know %s GwPortPrefixes ...", unicode(prefix), len(gwportprefixes)) _add_edge(gwportprefixes) else: fictive_gwportprefix = stubs.GwPortPrefix() fictive_netbox = stubs.Netbox() if gwportprefixes[0].prefix.vlan.net_ident: fictive_netbox.sysname = unicode( gwportprefixes[0].prefix.vlan.net_ident) else: fictive_netbox.sysname = unicode( gwportprefixes[0].interface.ifalias) fictive_netbox.category_id = 'elink' fictive_netbox.id = fictive_netbox.sysname fictive_interface = stubs.Interface() fictive_interface.netbox = fictive_netbox fictive_interface.ifname = (u"N/A (peer of %s)" % gwportprefixes[0].gw_ip) fictive_interface.speed = None fictive_gwportprefix.interface = fictive_interface fictive_gwportprefix.gw_ip = fictive_netbox.sysname fictive_gwportprefix.prefix = prefix graph.add_edge(gwportprefixes[0], fictive_gwportprefix, key=prefix) else: _add_edge(gwportprefixes) return graph
def setUp(self): """ Build a graph with the following network: node a and node b connected with a linknet prefix 158.38.0.0/30 with it's first pair of interfaces {a1: 1, b1: 2} node a and node c connected with a linknet prefix 158.38.0.4/30 with it's second pair of interfaces {a3: 5, c3: 6} node b and node d and node e connected as a core linknet on prefix 158.38.1.0/29 {b4: 1, d4: 2, , e4: 3} node f connected to unknown node (elink) as a linknet on prefix 158.38.99.0/30 { f5: 1, UNKNOWN } """ super(TopologyLayer3TestCase, self).setUp() self.model_id = 1 self.nav_graph = nx.MultiGraph() self.net_type_link = NetType(id=1, description='link') self.net_type_elink = NetType(id=2, description='elink') self.net_type_core = NetType(id=3, description='core') self.a = a = self._netbox_factory('a') self.b = b = self._netbox_factory('b') self.c = c = self._netbox_factory('c') self.d = d = self._netbox_factory('d') self.e = e = self._netbox_factory('e') self.f = f = self._netbox_factory('f') self.a1 = a1 = self._interface_factory('a1', a) self.a3 = a3 = self._interface_factory('a3', a) self.b1 = b1 = self._interface_factory('b1', b) self.b4 = b4 = self._interface_factory('b4', b) self.c3 = c3 = self._interface_factory('c3', c) self.d4 = d4 = self._interface_factory('d4', d) self.e4 = e4 = self._interface_factory('e4', e) self.f5 = f5 = self._interface_factory('f5', f) self.prefix_foo = Prefix( id=1111, net_address="158.38.0.0/30", vlan=Vlan(id=2111, vlan=50, net_type=self.net_type_link) ) self.prefix_bar_vlan = Vlan(id=2112, vlan=50, net_type=self.net_type_link) self.prefix_bar = Prefix( id=1112, net_address="158.38.0.4/30", vlan=self.prefix_bar_vlan ) self.prefix_bar_ipv6 = Prefix( id=6112, net_address="feed:dead:cafe:babe::/64", vlan=self.prefix_bar_vlan ) self.prefix_baz = Prefix( id=1113, net_address="158.38.1.0/29", vlan=Vlan(id=2113, vlan=50, net_type=self.net_type_core) ) self.prefix_zar = Prefix( id=1114, net_address="158.38.99.0/30", vlan=Vlan(id=2114, vlan=50, net_type=self.net_type_elink) ) # fictive netbox, interface, gwportprefix for F to UNKNOWN self.unknown = unknown = stubs.Netbox() unknown.sysname = 'fictive netbox' unknown.category_id = 'elink' self.unknown_interface = unknown_interface = stubs.Interface() self.unknown_interface.netbox = unknown self.unknown_interface.ifname = "?" self.unknown_interface.speed = None #node a and node b connected with a linknet prefix 158.38.0.0/30 # with it's first pair of interfaces {a1: 1, b1: 2} self.linknet_a1_for_a_b = GwPortPrefix( interface=self.a1, prefix=self.prefix_foo, gw_ip='158.38.0.1', virtual=False ) self.linknet_b1_for_a_b = GwPortPrefix( interface=self.b1, prefix=self.prefix_foo, gw_ip='158.38.0.2', virtual=False ) self._add_edge(self.nav_graph, self.linknet_a1_for_a_b, self.linknet_b1_for_a_b, self.prefix_foo) self._add_edge(self.nav_graph, self.linknet_b1_for_a_b, self.linknet_a1_for_a_b, self.prefix_foo) #node a and node c connected with a linknet prefix 158.38.0.4/30 # with it's second pair of interfaces {a2: 5, c2: 6} self.linknet_a3_for_a_c = GwPortPrefix( interface=self.a3, prefix=self.prefix_bar, gw_ip='158.38.0.5', virtual=False ) self.linknet_c3_for_a_c = GwPortPrefix( interface=self.c3, prefix=self.prefix_bar, gw_ip='158.38.0.6', virtual=False ) self.linknet_v6_a3_for_a_c = GwPortPrefix( interface=self.a3, prefix=self.prefix_bar_ipv6, gw_ip='feed:dead:cafe:babe::5', virtual=False ) self.linknet_v6_c3_for_a_c = GwPortPrefix( interface=self.c3, prefix=self.prefix_bar_ipv6, gw_ip='feed:dead:cafe:babe::3', virtual=False ) # v4 prefix self._add_edge(self.nav_graph, self.linknet_a3_for_a_c, self.linknet_c3_for_a_c, self.prefix_bar) self._add_edge(self.nav_graph, self.linknet_c3_for_a_c, self.linknet_a3_for_a_c, self.prefix_bar) # v6 prefix self._add_edge(self.nav_graph, self.linknet_v6_a3_for_a_c, self.linknet_v6_c3_for_a_c, self.prefix_bar_ipv6) self._add_edge(self.nav_graph, self.linknet_v6_c3_for_a_c, self.linknet_v6_a3_for_a_c, self.prefix_bar_ipv6) #node b and node d and node e connected as a core linknet on prefix # 158.38.1.0/29 {b3: 1, d3: 2, , e3: 3} self.linknet_b4_for_b_d_e = GwPortPrefix( interface=self.b4, prefix=self.prefix_baz, gw_ip='158.38.1.1', virtual=False ) self.linknet_d4_for_b_d_e = GwPortPrefix( interface=self.d4, prefix=self.prefix_baz, gw_ip='158.38.1.2', virtual=False ) self.linknet_e4_for_b_d_e = GwPortPrefix( interface=self.e4, prefix=self.prefix_baz, gw_ip='158.38.1.3', virtual=False ) # core is a STAR. # b3, d3 self._add_edge(self.nav_graph, self.linknet_b4_for_b_d_e, self.linknet_d4_for_b_d_e, self.prefix_baz ) self._add_edge(self.nav_graph, self.linknet_b4_for_b_d_e, self.linknet_e4_for_b_d_e, self.prefix_baz ) self._add_edge(self.nav_graph, self.linknet_d4_for_b_d_e, self.linknet_b4_for_b_d_e, self.prefix_baz ) self._add_edge(self.nav_graph, self.linknet_d4_for_b_d_e, self.linknet_e4_for_b_d_e, self.prefix_baz ) self._add_edge(self.nav_graph, self.linknet_e4_for_b_d_e, self.linknet_b4_for_b_d_e, self.prefix_baz ) self._add_edge(self.nav_graph, self.linknet_e4_for_b_d_e, self.linknet_d4_for_b_d_e, self.prefix_baz ) #node f connected to unknown node (elink) as a linknet on prefix # 158.38.99.0/30 { f4: 1, UNKNOWN } self.linknet_f5_for_f_unknown = GwPortPrefix( interface=self.f5, prefix=self.prefix_zar, gw_ip='158.38.99.1', virtual=False ) self.linknet_unknown4_for_f_unknown = stubs.GwPortPrefix() self.linknet_unknown4_for_f_unknown.interface = unknown_interface self.linknet_unknown4_for_f_unknown.gw_ip = unknown.sysname self.linknet_unknown4_for_f_unknown.prefix = self.prefix_zar self._add_edge(self.nav_graph, self.linknet_f5_for_f_unknown, self.linknet_unknown4_for_f_unknown, self.prefix_zar) self._add_edge(self.nav_graph, self.linknet_unknown4_for_f_unknown, self.linknet_f5_for_f_unknown, self.prefix_zar) self.vlans = patch.object(topology, '_get_vlans_map_layer3', return_value={ 2111: [self.prefix_foo], 2112: [self.prefix_bar, self.prefix_bar_ipv6], 2113: [self.prefix_baz], 2114: [self.prefix_zar] }) self.vlans.start() self.l3 = patch.object(vlan, 'build_layer3_graph', return_value=self.nav_graph) self.l3.start() self.netmap_graph = topology.build_netmap_layer3_graph(self.nav_graph, None)