fix: handle plain HttpResponse from elevation SVG endpoint, unracked devices
- RackElevationOfflineSVGView called .render() unconditionally on the RackViewSet elevation action's response, but that action returns a plain HttpResponse (not a DRF Response) when render=svg is requested, causing AttributeError on every click. - effective_width_positions() crashed with decimal.InvalidOperation when a device had no rack position set (e.g. surfaced via NetBox Topology Views, which includes unracked devices). Such devices now get the default full-width result instead of entering the Decimal conversion. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
from netbox.plugins import PluginConfig, get_plugin_config
|
from netbox.plugins import PluginConfig, get_plugin_config
|
||||||
|
|
||||||
__version__ = "0.11.1"
|
__version__ = "0.11.2"
|
||||||
|
|
||||||
|
|
||||||
class NetBoxUtilitiesConfig(PluginConfig):
|
class NetBoxUtilitiesConfig(PluginConfig):
|
||||||
|
|||||||
@@ -122,6 +122,9 @@ def effective_width_positions(devices, placements=None):
|
|||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
|
if device.position is None:
|
||||||
|
result.setdefault(device.pk, (FULL_WIDTH, 1, "default"))
|
||||||
|
continue
|
||||||
key = (
|
key = (
|
||||||
device.rack_id,
|
device.rack_id,
|
||||||
device.face,
|
device.face,
|
||||||
|
|||||||
@@ -191,6 +191,34 @@ class RackWidthTest(SimpleTestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_ignores_unracked_devices_without_a_position(self):
|
||||||
|
devices = [
|
||||||
|
SimpleNamespace(
|
||||||
|
pk=41,
|
||||||
|
rack_id=None,
|
||||||
|
face="front",
|
||||||
|
position=None,
|
||||||
|
device_type=SimpleNamespace(u_height=1),
|
||||||
|
netbox_utilities_rack_placement=None,
|
||||||
|
),
|
||||||
|
SimpleNamespace(
|
||||||
|
pk=42,
|
||||||
|
rack_id=3,
|
||||||
|
face="front",
|
||||||
|
position=11,
|
||||||
|
device_type=SimpleNamespace(u_height=1),
|
||||||
|
netbox_utilities_rack_placement=None,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
effective_width_positions(devices),
|
||||||
|
{
|
||||||
|
41: (1, 1, "default"),
|
||||||
|
42: (1, 1, "default"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_adds_rack_width_fields_to_existing_location_fieldset(self):
|
def test_adds_rack_width_fields_to_existing_location_fieldset(self):
|
||||||
from utilities.forms.rendering import FieldSet
|
from utilities.forms.rendering import FieldSet
|
||||||
|
|
||||||
|
|||||||
@@ -391,7 +391,8 @@ class RackElevationOfflineSVGView(LoginRequiredMixin, View):
|
|||||||
api_request.user = request.user
|
api_request.user = request.user
|
||||||
drf_view = RackViewSet.as_view({"get": "elevation"})
|
drf_view = RackViewSet.as_view({"get": "elevation"})
|
||||||
response = drf_view(api_request, pk=rack.pk)
|
response = drf_view(api_request, pk=rack.pk)
|
||||||
response.render()
|
if hasattr(response, "render"):
|
||||||
|
response.render()
|
||||||
|
|
||||||
offline_svg = strip_svg_hyperlinks(response.content)
|
offline_svg = strip_svg_hyperlinks(response.content)
|
||||||
filename = f"{rack.name}-{face}-offline.svg".replace(" ", "_")
|
filename = f"{rack.name}-{face}-offline.svg".replace(" ", "_")
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "netbox-utilities"
|
name = "netbox-utilities"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
description = "Navigation, tenant utilities, connection VLANs, partial-width racks, and bulk operations for NetBox 4.6/4.7"
|
description = "Navigation, tenant utilities, connection VLANs, partial-width racks, and bulk operations for NetBox 4.6/4.7"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
Reference in New Issue
Block a user