55from dcim .models import *
66from ipam .models import *
77from circuits .models import *
8- from .models import SavedTopology
8+ from extras .models import SavedFilter
99from . import forms , filters
1010from django .contrib .auth .mixins import PermissionRequiredMixin
1111from django .conf import settings
@@ -247,101 +247,6 @@ def filter_tags(tags):
247247 tags = filtered_tags
248248 return tags
249249
250- def get_vlan_topology (nb_devices_qs , vlans ):
251-
252- topology_dict = {'nodes' : [], 'edges' : []}
253- device_roles = set ()
254- all_device_tags = set ()
255- multi_cable_connections = []
256- vlan = VLAN .objects .get (id = vlans )
257- interfaces = vlan .get_interfaces ()
258- filtred_devices = [d .id for d in nb_devices_qs ]
259- filtred_interfaces = []
260- for interface in interfaces :
261- if interface .is_connectable :
262- direct_device_id = interface .device .id
263- interface_trace = interface .trace ()
264- if len (interface_trace ) != 0 :
265- termination_b_iface = interface_trace [- 1 ][- 1 ]
266- connected_device_id = termination_b_iface .device .id
267- if (direct_device_id in filtred_devices ) or (direct_device_id in filtred_devices ):
268- filtred_interfaces .append (interface )
269-
270-
271-
272- devices = []
273- for interface in filtred_interfaces :
274- if interface .is_connectable :
275- if interface .device not in devices :
276- devices .append (interface .device )
277- interface_trace = interface .trace ()
278- if len (interface_trace ) != 0 :
279- termination_b_iface = interface_trace [- 1 ][- 1 ]
280- if termination_b_iface .device not in devices :
281- devices .append (termination_b_iface .device )
282-
283-
284- device_ids = [d .id for d in devices ]
285- for device in devices :
286- device_is_passive = False
287- device_url = device .get_absolute_url ()
288- if NETBOX_CURRENT_VERSION >= version .parse ("4.0.0" ):
289- device_role_obj = device .role
290- else :
291- device_role_obj = device .device_role
292- primary_ip = ''
293- if device .primary_ip :
294- primary_ip = str (device .primary_ip .address )
295- tags = [str (tag ) for tag in device .tags .names ()]
296- for tag in tags :
297- all_device_tags .add ((tag , not tag_is_hidden (tag )))
298- topology_dict ['nodes' ].append ({
299- 'id' : device .name ,
300- 'name' : device .name ,
301- 'label' : {'text' : device .name },
302- 'dcimDeviceLink' : device_url ,
303- 'primaryIP' : primary_ip ,
304- 'serial_number' : device .serial ,
305- 'model' : device .device_type .model ,
306- 'deviceRole' : device_role_obj .slug ,
307- 'layer' : get_node_layer_sort_preference (
308- device_role_obj .slug
309- ),
310- 'iconName' : get_icon_type (
311- device .id
312- ),
313- 'isPassive' : device_is_passive ,
314- 'tags' : tags ,
315- })
316- is_visible = not (device_role_obj .slug in UNDISPLAYED_DEVICE_ROLE_SLUGS )
317- device_roles .add ((device_role_obj .slug , device_role_obj .name , is_visible ))
318-
319- mapped_links = []
320- for interface in filtred_interfaces :
321- if interface .is_connectable :
322- interface_trace = interface .trace ()
323- if len (interface_trace ) != 0 :
324- source_cable = interface_trace [0 ]
325- dest_cable = interface_trace [- 1 ]
326- mapping_link = [source_cable [0 ].device .id ,dest_cable [- 1 ].device .id ]
327- if (mapping_link not in mapped_links ) and (mapping_link .reverse () not in mapped_links ):
328- mapped_links .append (mapping_link )
329-
330- topology_dict ['edges' ].append ({
331- 'id' : source_cable [1 ].id ,
332- 'dcimCableURL' : source_cable [1 ].get_absolute_url (),
333- 'label' : f"Cable { source_cable [1 ].id } " ,
334- 'source' : source_cable [0 ].device .name ,
335- 'target' : dest_cable [- 1 ].device .name ,
336- 'sourceDeviceName' : source_cable [0 ].device .name ,
337- 'targetDeviceName' : dest_cable [- 1 ].device .name ,
338- "srcIfName" : if_shortname (source_cable [0 ].name ),
339- "tgtIfName" : if_shortname (dest_cable [- 1 ].name ),
340- })
341-
342- return topology_dict , device_roles , multi_cable_connections , list (all_device_tags )
343-
344-
345250def get_topology (nb_devices_qs , params ):
346251 display_unconnected = params .get ('display_unconnected' )
347252 display_passive = params .get ('display_passive' )
@@ -403,7 +308,7 @@ def get_topology(nb_devices_qs, params):
403308 node_data = {
404309 'id' : f'device-{ nb_device .id } ' ,
405310 'name' : nb_device .name ,
406- 'label' : { 'text' : nb_device .name } ,
311+ 'label' : nb_device .name ,
407312 'layer' : get_node_layer_sort_preference (
408313 device_role_obj .slug
409314 ),
@@ -460,8 +365,10 @@ def get_topology(nb_devices_qs, params):
460365 "label" : f"Cable { link .id } " ,
461366 "source" : f"device-{ link .a_terminations [0 ].device .id } " ,
462367 "target" : f"device-{ link .b_terminations [0 ].device .id } " ,
463- "sourceInterfaceLabel" : if_shortname (link .a_terminations [0 ].name ),
464- "targetInterfaceLabel" : if_shortname (link .b_terminations [0 ].name ),
368+ "sourceInterface" : link .a_terminations [0 ].name ,
369+ "sourceInterfaceLabel" : {'text' : if_shortname (link .a_terminations [0 ].name )},
370+ "targetInterface" : link .b_terminations [0 ].name ,
371+ "targetInterfaceLabel" : {'text' : if_shortname (link .b_terminations [0 ].name )},
465372 "customAttributes" : {
466373 "name" : f"Cable { link .id } " ,
467374 "dcimCableURL" : link_url ,
@@ -512,10 +419,12 @@ def get_topology(nb_devices_qs, params):
512419 source_device_id = f"device-{ side_a_interface .device .id } "
513420 target_device_id = f"device-{ side_b_interface .device .id } "
514421 topology_dict ['edges' ].append ({
515- 'source' : source_device_id ,
516- 'target' : target_device_id ,
517- "sourceInterfaceLabel" : if_shortname (side_a_interface .name ),
518- "targetInterfaceLabel" : if_shortname (side_b_interface .name ),
422+ "source" : source_device_id ,
423+ "target" : target_device_id ,
424+ "sourceInterface" : link .a_terminations [0 ].name ,
425+ "sourceInterfaceLabel" : {'text' : if_shortname (link .a_terminations [0 ].name )},
426+ "targetInterface" : link .b_terminations [0 ].name ,
427+ "targetInterfaceLabel" : {'text' : if_shortname (link .b_terminations [0 ].name )},
519428 "isLogicalMultiCable" : True ,
520429 "customAttributes" : {
521430 "name" : f"Multi-Cable Connection" ,
@@ -527,40 +436,6 @@ def get_topology(nb_devices_qs, params):
527436 return topology_dict , device_roles , multi_cable_connections , all_device_tags
528437
529438
530- def get_saved_topology (id ):
531- topology_dict = {}
532- device_roles = []
533- device_tags = []
534- device_roles_detailed = []
535- device_tags_detailed = []
536- layout_context = {}
537- topology_data = SavedTopology .objects .get (id = id )
538- if not topology_data :
539- return topology_dict , device_roles , device_tags , layout_context
540- topology_dict = dict (topology_data .topology )
541- if 'nodes' not in topology_dict :
542- return topology_dict , device_roles , device_tags , layout_context
543- device_roles = list (set ([str (d .get ('deviceRole' )) for d in topology_dict ['nodes' ] if d .get ('deviceRole' )]))
544- for device_role in device_roles :
545- is_visible = not (device_role in UNDISPLAYED_DEVICE_ROLE_SLUGS )
546- device_role_obj = DeviceRole .objects .get (slug = device_role )
547- if not device_role_obj :
548- device_roles_detailed .append ((device_role , device_role , is_visible ))
549- continue
550- device_roles_detailed .append ((device_role_obj .slug , device_role_obj .name , is_visible ))
551- device_roles_detailed .sort (key = lambda i : get_node_layer_sort_preference (i [0 ]))
552- device_tags = set ()
553- for device in topology_dict ['nodes' ]:
554- if 'tags' not in device :
555- continue
556- for tag in device ['tags' ]:
557- device_tags .add (str (tag ))
558- device_tags = list (device_tags )
559- device_tags_detailed = list ([(tag , not tag_is_hidden (tag )) for tag in device_tags ])
560- layout_context = dict (topology_data .layout_context )
561- return topology_dict , device_roles_detailed , device_tags_detailed , layout_context
562-
563-
564439class TopologyView (PermissionRequiredMixin , View ):
565440 """Generic Topology View"""
566441 permission_required = ('dcim.view_site' , 'dcim.view_device' , 'dcim.view_cable' )
@@ -570,45 +445,39 @@ class TopologyView(PermissionRequiredMixin, View):
570445
571446 def get (self , request ):
572447
573- if not request .GET :
574- self . queryset = Device . objects . none ()
575- elif 'saved_topology_id' in request . GET :
448+ clean_request = request .GET . copy ()
449+
450+ if not clean_request :
576451 self .queryset = Device .objects .none ()
577452
578- display_unconnected = request .GET .get ('display_unconnected' )
579- if display_unconnected is not None :
580- display_unconnected = display_unconnected .lower == 'true'
453+ self .queryset = self .filterset (clean_request , self .queryset ).qs
581454
582- display_passive = request .GET .get ('display_passive' )
583- if display_passive is not None :
584- display_passive = display_passive .lower () == 'true'
455+ saved_filter = None
456+ if 'filter_id' in clean_request and clean_request ['filter_id' ]:
457+ filter_id = clean_request ['filter_id' ]
458+ saved_filter = SavedFilter .objects .get (pk = filter_id )
459+
460+ if saved_filter :
461+ # Extract only plugin-specific filters from the SavedFilter.
462+ # All NetBox-native filters are handled by filtersets.
463+ display_unconnected = saved_filter .parameters .get ('display_unconnected' , [DISPLAY_UNCONNECTED ])[0 ]
464+ display_passive = saved_filter .parameters .get ('display_passive' , [DISPLAY_PASSIVE_DEVICES ])[0 ]
585465 else :
466+ display_unconnected = DISPLAY_UNCONNECTED
586467 display_passive = DISPLAY_PASSIVE_DEVICES
587468
588- params = {
589- 'display_unconnected' : display_unconnected ,
590- 'display_passive' : display_passive ,
591- }
469+ if clean_request .get ('display_unconnected' ) is not None :
470+ display_unconnected = clean_request .get ('display_unconnected' )
592471
593- saved_topology_id = request . GET . get ('saved_topology_id' )
594- layout_context = {}
472+ if clean_request . get ('display_passive' ) is not None :
473+ display_passive = clean_request . get ( 'display_passive' )
595474
596- if saved_topology_id is not None :
597- topology_dict , device_roles , device_tags , layout_context = get_saved_topology (saved_topology_id )
598- else :
599- vlans = []
600- if 'vlan_id' in request .GET :
601- clean_request = request .GET .copy ()
602- clean_request .pop ('vlan_id' )
603- vlans = request .GET .get ('vlan_id' )
604- else :
605- clean_request = request .GET .copy ()
475+ params = {
476+ 'display_unconnected' : str (display_unconnected ).lower () == 'true' ,
477+ 'display_passive' : str (display_passive ).lower () == 'true' ,
478+ }
606479
607- self .queryset = self .filterset (clean_request , self .queryset ).qs
608- if len (vlans ) == 0 :
609- topology_dict , device_roles , multi_cable_connections , device_tags = get_topology (self .queryset , params )
610- else :
611- topology_dict , device_roles , multi_cable_connections , device_tags = get_vlan_topology (self .queryset , vlans )
480+ topology_dict , device_roles , multi_cable_connections , device_tags = get_topology (self .queryset , params )
612481
613482 return render (request , self .template_name , {
614483 'source_data' : json .dumps (topology_dict ),
0 commit comments