import logging from django.apps import apps from django.core.exceptions import ObjectDoesNotExist from django.db import OperationalError, ProgrammingError from netbox.plugins import get_plugin_config from .rack_width import effective_width_positions logger = logging.getLogger(__name__) TOPOLOGY_RACK_ELEVATION_VIEW = "plugins:netbox_topology_views:rack_elevation" def is_topology_rack_elevation_request(request): resolver_match = getattr(request, "resolver_match", None) return getattr(resolver_match, "view_name", None) == TOPOLOGY_RACK_ELEVATION_VIEW def topology_rack_width_enabled(request): """Return whether the optional Topology Views rack integration applies.""" return bool( is_topology_rack_elevation_request(request) and get_plugin_config("netbox_utilities", "topology_views_rack_width_enabled") and apps.is_installed("netbox_topology_views") ) def serialize_topology_placement(placement): return serialize_topology_width( placement.device, placement.width, placement.horizontal_position, source="stored", ) def serialize_topology_width(device, width, horizontal_position, *, source): width = int(width) horizontal_position = int(horizontal_position) return { "device_id": device.pk, "url": device.get_absolute_url(), "width": width, "horizontal_position": horizontal_position, "left_percent": round((horizontal_position - 1) / width * 100, 8), "width_percent": round(100 / width, 8), "width_source": source, } def get_topology_rack_width_data(request): """Return permitted partial-width devices shown by Topology Views.""" if not topology_rack_width_enabled(request): return None data = {"devices": [], "status": "ready", "schema_version": 3, "complete": False} if not request.GET: return data from dcim.models import Device, Rack try: racks = Rack.objects.restrict(request.user, "view") selected_racks = request.GET.getlist("rack_id") if selected_racks: racks = racks.filter(pk__in=selected_racks) else: selected_sites = request.GET.getlist("site_id") selected_locations = request.GET.getlist("location_id") if selected_sites: racks = racks.filter(site_id__in=selected_sites) if selected_locations: racks = racks.filter(location_id__in=selected_locations) devices = list( Device.objects.restrict(request.user, "view") .filter( rack_id__in=racks.values("pk"), position__gt=0, device_type__u_height__gt=0, ) .select_related("device_type", "netbox_utilities_rack_placement") .order_by("rack_id", "face", "position", "pk") ) widths = effective_width_positions(devices) data["devices"] = [ serialize_topology_width(device, width, position, source=source) for device in devices for width, position, source in (widths[device.pk],) if width > 1 ] data["complete"] = True return data except ( AttributeError, ObjectDoesNotExist, OperationalError, ProgrammingError, TypeError, ValueError, ): # Keep NetBox usable while plugin migrations are being installed. logger.warning( "Could not load partial rack widths for NetBox Topology Views path %s; using its native rack layout", request.get_full_path(), exc_info=True, ) data["status"] = "native-fallback" return data