import logging
import re
from html import unescape
from django.apps import apps
from django.core.exceptions import ObjectDoesNotExist
from django.db import OperationalError, ProgrammingError
from django.templatetags.static import static
from django.utils.html import escape, json_script
from netbox.plugins import get_plugin_config
from . import __version__
from .rack_width import effective_width_positions
logger = logging.getLogger(__name__)
TOPOLOGY_RACK_ELEVATION_VIEW = "plugins:netbox_topology_views:rack_elevation"
RACK_DEVICE_TAG_RE = re.compile(
r"]*\bclass=[\"'][^\"']*\brack-device\b[^\"']*[\"'][^>]*>",
re.IGNORECASE,
)
HREF_RE = re.compile(r"\bhref=[\"'](?P[^\"']+)[\"']", re.IGNORECASE)
STYLE_RE = re.compile(r"\bstyle=(?P[\"'])(?P'
payload = json_script(data, "netbox-utilities-topology-rack-width-data")
script_url = escape(f"{static('netbox_utilities/topology-rack-width.js')}?v={__version__}")
return f'{stylesheet}{payload}'
def _apply_topology_widths_to_device_tags(html, data):
descriptors = {device["url"]: device for device in data["devices"]}
def replace_tag(match):
tag = match.group(0)
href_match = HREF_RE.search(tag)
if href_match is None:
return tag
descriptor = descriptors.get(unescape(href_match.group("url")))
if descriptor is None:
return tag
geometry = (
"right:auto!important;"
f"left:calc({descriptor['left_percent']}% + 3px)!important;"
f"width:calc({descriptor['width_percent']}% - 6px)!important"
)
style_match = STYLE_RE.search(tag)
if style_match is not None:
quote = style_match.group("quote")
existing = style_match.group("style").rstrip().rstrip(";")
replacement = f"style={quote}{existing};{geometry}{quote}"
return f"{tag[: style_match.start()]}{replacement}{tag[style_match.end() :]}"
return f'{tag[:-1]} style="{geometry}">'
return RACK_DEVICE_TAG_RE.sub(replace_tag, html)
def _inject_topology_widths(response):
if (
getattr(response, "streaming", False)
or getattr(response, "status_code", None) != 200
or "text/html" not in response.get("Content-Type", "")
):
return response
html = response.content.decode(response.charset or "utf-8")
if "" not in html.lower() or "netbox-utilities-topology-rack-width-data" in html:
return response
visible_urls = _visible_topology_device_urls(html)
data = _topology_data_for_visible_devices(visible_urls)
html = _apply_topology_widths_to_device_tags(html, data)
insertion = _topology_width_head(data)
head_end = html.lower().index("")
html = f"{html[:head_end]}{insertion}{html[head_end:]}"
content = html.encode(response.charset or "utf-8")
response.content = content
if response.has_header("Content-Length"):
response["Content-Length"] = str(len(content))
return response
def apply_topology_rack_widths(request, response):
"""Apply widths after the optional view has rendered its authorized devices."""
if not topology_rack_width_enabled(request):
return response
try:
return _inject_topology_widths(response)
except Exception:
logger.warning(
"Could not inject partial rack widths into NetBox Topology Views path %s",
request.get_full_path(),
exc_info=True,
)
return response