fix: preserve native reorder rack devices
This commit is contained in:
@@ -117,6 +117,10 @@ Reorder ersetzt seine nativen Gerätekacheln erst, nachdem ein vollständiger
|
|||||||
und zur JavaScript-Version passender Datensatz geprüft wurde. Bei einer
|
und zur JavaScript-Version passender Datensatz geprüft wurde. Bei einer
|
||||||
gemischten Installation aus altem Python-Code und neuen statischen Dateien
|
gemischten Installation aus altem Python-Code und neuen statischen Dateien
|
||||||
bleibt deshalb die native Ansicht erhalten, statt ein leeres Rack zu zeigen.
|
bleibt deshalb die native Ansicht erhalten, statt ein leeres Rack zu zeigen.
|
||||||
|
Ab Version `0.9.4` werden vorhandene Reorder-Kacheln außerdem direkt auf das
|
||||||
|
12-Spalten-Raster erweitert und nicht mehr vorsorglich neu erzeugt. Nur ein
|
||||||
|
zweites, von NetBox' Ein-Gerät-pro-HE-Darstellung unterschlagenes Gerät wird
|
||||||
|
aus den Plugin-Daten ergänzt. Das verhindert browserabhängige leere Racks.
|
||||||
Bereits vorhandene Geräte, die dieselbe HE und Rackseite belegen, aber noch
|
Bereits vorhandene Geräte, die dieselbe HE und Rackseite belegen, aber noch
|
||||||
keine Plugin-Platzierungszeile besitzen, werden in Rack-SVG, Reorder und der
|
keine Plugin-Platzierungszeile besitzen, werden in Rack-SVG, Reorder und der
|
||||||
Topology-Rack-Ansicht ohne Datenbankänderung gleichmäßig nebeneinander
|
Topology-Rack-Ansicht ohne Datenbankänderung gleichmäßig nebeneinander
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from netbox.plugins import PluginConfig, get_plugin_config
|
from netbox.plugins import PluginConfig, get_plugin_config
|
||||||
|
|
||||||
__version__ = "0.9.3"
|
__version__ = "0.9.4"
|
||||||
|
|
||||||
|
|
||||||
class NetBoxUtilitiesConfig(PluginConfig):
|
class NetBoxUtilitiesConfig(PluginConfig):
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
console.warn('NetBox Utilities received no replacement devices and kept the populated native layout.');
|
console.warn('NetBox Utilities received no replacement devices and kept the populated native layout.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const devicesById = new Map(data.devices.map(device => [String(device.id), device]));
|
||||||
|
const nativeItemsByFace = {};
|
||||||
|
|
||||||
const setAttribute = (element, name, value) => element.setAttribute(name, String(value));
|
const setAttribute = (element, name, value) => element.setAttribute(name, String(value));
|
||||||
const originalFetch = window.fetch.bind(window);
|
const originalFetch = window.fetch.bind(window);
|
||||||
@@ -73,12 +75,26 @@
|
|||||||
|
|
||||||
Object.entries(gridElements).forEach(([face, grid]) => {
|
Object.entries(gridElements).forEach(([face, grid]) => {
|
||||||
setAttribute(grid, 'gs-column', columns);
|
setAttribute(grid, 'gs-column', columns);
|
||||||
|
const nativeItemsById = new Map(
|
||||||
|
Array.from(grid.querySelectorAll('.grid-stack-item')).map(item => [item.getAttribute('gs-id'), item]),
|
||||||
|
);
|
||||||
grid.querySelectorAll('.grid-stack-item').forEach(item => {
|
grid.querySelectorAll('.grid-stack-item').forEach(item => {
|
||||||
// The native Reorder view is based on Rack.get_rack_units(), which can
|
// Keep every native widget whenever possible. Rack.get_rack_units() can
|
||||||
// expose only one device per rack unit. Rebuild the mounted grids from
|
// expose only one device per unit, so missing shared widgets are added
|
||||||
// the complete server payload; keep native non-racked devices intact.
|
// from the complete payload below.
|
||||||
if (face !== 'other') {
|
if (face !== 'other') {
|
||||||
item.remove();
|
const device = devicesById.get(item.getAttribute('gs-id'));
|
||||||
|
const belongsOnFace = device && (device.face === face || device.full_depth);
|
||||||
|
if (!belongsOnFace) {
|
||||||
|
item.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAttribute(item, 'gs-w', device.grid_width);
|
||||||
|
setAttribute(item, 'gs-h', device.grid_height);
|
||||||
|
setAttribute(item, 'gs-x', device.grid_x);
|
||||||
|
setAttribute(item, 'gs-y', device.grid_y);
|
||||||
|
item.dataset.rackWidth = String(device.width);
|
||||||
|
item.dataset.horizontalPosition = String(device.horizontal_position);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAttribute(item, 'gs-w', columns);
|
setAttribute(item, 'gs-w', columns);
|
||||||
@@ -86,6 +102,7 @@
|
|||||||
item.dataset.rackWidth = '1';
|
item.dataset.rackWidth = '1';
|
||||||
item.dataset.horizontalPosition = '1';
|
item.dataset.horizontalPosition = '1';
|
||||||
});
|
});
|
||||||
|
nativeItemsByFace[face] = nativeItemsById;
|
||||||
});
|
});
|
||||||
|
|
||||||
const addWidget = (device, gridFace, itemFace, rearSide = false) => {
|
const addWidget = (device, gridFace, itemFace, rearSide = false) => {
|
||||||
@@ -125,9 +142,14 @@
|
|||||||
|
|
||||||
data.devices.forEach(device => {
|
data.devices.forEach(device => {
|
||||||
const face = device.face === 'rear' ? 'rear' : 'front';
|
const face = device.face === 'rear' ? 'rear' : 'front';
|
||||||
addWidget(device, face, face);
|
if (!nativeItemsByFace[face].has(String(device.id))) {
|
||||||
|
addWidget(device, face, face);
|
||||||
|
}
|
||||||
if (device.full_depth) {
|
if (device.full_depth) {
|
||||||
addWidget(device, face === 'front' ? 'rear' : 'front', 'back', true);
|
const oppositeFace = face === 'front' ? 'rear' : 'front';
|
||||||
|
if (!nativeItemsByFace[oppositeFace].has(String(device.id))) {
|
||||||
|
addWidget(device, oppositeFace, 'back', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -291,6 +291,9 @@ class ReorderRackFrontendTest(SimpleTestCase):
|
|||||||
self.assertIn("data.schema_version !== 3", script)
|
self.assertIn("data.schema_version !== 3", script)
|
||||||
self.assertIn("data.complete !== true", script)
|
self.assertIn("data.complete !== true", script)
|
||||||
self.assertIn("nativeMountedItems.length", script)
|
self.assertIn("nativeMountedItems.length", script)
|
||||||
|
self.assertIn("const nativeItemsByFace = {}", script)
|
||||||
|
self.assertIn("const device = devicesById.get", script)
|
||||||
|
self.assertIn("if (!nativeItemsByFace[face].has", script)
|
||||||
self.assertNotIn("partialDeviceIds", script)
|
self.assertNotIn("partialDeviceIds", script)
|
||||||
self.assertNotIn("JSON.stringify", script)
|
self.assertNotIn("JSON.stringify", script)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "netbox-utilities"
|
name = "netbox-utilities"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
description = "Navigation, tenant utilities, partial-width rack devices, bulk uploads, and rack reordering for NetBox 4.6"
|
description = "Navigation, tenant utilities, partial-width rack devices, bulk uploads, and rack reordering for NetBox 4.6"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
Reference in New Issue
Block a user