Skip to content

Commit c8369fa

Browse files
authored
NetBox 4.0 Compatibility Fix (#144)
1 parent 2c8afab commit c8369fa

File tree

7 files changed

+102
-21
lines changed

7 files changed

+102
-21
lines changed

nextbox_ui_plugin/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
from extras.plugins import PluginConfig
1+
from packaging import version
2+
from django.conf import settings
3+
NETBOX_CURRENT_VERSION = version.parse(settings.VERSION)
4+
5+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
6+
from netbox.plugins import PluginConfig
7+
else:
8+
from extras.plugins import PluginConfig
29

310
class NextBoxUIConfig(PluginConfig):
411
name = 'nextbox_ui_plugin'
512
verbose_name = 'NextBox UI'
613
description = 'A topology visualization plugin for Netbox powered by NextUI Toolkit.'
7-
version = '0.14.0'
14+
version = '0.15.0'
815
author = 'Igor Korotchenkov'
916
author_email = 'iDebugAll@gmail.com'
1017
base_url = 'nextbox-ui'

nextbox_ui_plugin/forms.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,24 @@
1515
if NETBOX_CURRENT_VERSION >= version.parse("3.0") :
1616
from django.utils.translation import gettext as _
1717

18-
if NETBOX_CURRENT_VERSION < version.parse("3.5"):
18+
if NETBOX_CURRENT_VERSION >= version.parse("4.0"):
19+
from utilities.forms.fields import (
20+
DynamicModelMultipleChoiceField,
21+
DynamicModelChoiceField
22+
)
23+
elif NETBOX_CURRENT_VERSION < version.parse("3.5"):
1924
from utilities.forms import (
20-
BootstrapMixin,
2125
DynamicModelMultipleChoiceField,
2226
DynamicModelChoiceField
2327
)
2428
else:
25-
from utilities.forms import BootstrapMixin
2629
from utilities.forms.fields import (
2730
DynamicModelMultipleChoiceField,
2831
DynamicModelChoiceField
2932
)
3033

3134

32-
class TopologyFilterForm(BootstrapMixin, forms.Form):
35+
class TopologyFilterForm(forms.Form):
3336

3437
model = Device
3538

@@ -71,7 +74,7 @@ class TopologyFilterForm(BootstrapMixin, forms.Form):
7174
region_id.label = _('Regions')
7275

7376

74-
class LoadSavedTopologyFilterForm(BootstrapMixin, forms.Form):
77+
class LoadSavedTopologyFilterForm(forms.Form):
7578

7679
def __init__(self, *args, **kwargs):
7780
user = kwargs.pop('user', None)

nextbox_ui_plugin/migrations/0001_initial.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,49 @@
22

33
from django.db import migrations, models
44
import django.db.models.deletion
5+
import netbox
6+
from django.db import connection
7+
from django.conf import settings
8+
from packaging import version
59

10+
NETBOX_CURRENT_VERSION = version.parse(settings.VERSION)
11+
12+
def get_user_model():
13+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
14+
return 'users.User'
15+
else:
16+
return 'users.NetBoxUser'
17+
18+
def update_foreign_key(apps, schema_editor):
19+
if NETBOX_CURRENT_VERSION < version.parse("4.0.0"):
20+
return
21+
UserModel = get_user_model()
22+
table_name = 'nextbox_ui_plugin_savedtopology'
23+
24+
with connection.cursor() as cursor:
25+
# Check the existing ForeignKey reference
26+
cursor.execute("""
27+
SELECT conname AS constraint_name, conrelid::regclass AS table_name, a.attname AS column_name,
28+
confrelid::regclass AS referenced_table_name, af.attname AS referenced_column_name
29+
FROM pg_constraint AS c
30+
JOIN pg_attribute AS a ON a.attnum = ANY(c.conkey)
31+
JOIN pg_class AS cl ON cl.oid = c.conrelid
32+
JOIN pg_namespace AS ns ON ns.oid = cl.relnamespace
33+
JOIN pg_attribute AS af ON af.attnum = ANY(c.confkey)
34+
WHERE ns.nspname = 'public' AND cl.relname = %s AND a.attname = 'created_by_id' AND c.contype = 'f';
35+
""", [table_name])
36+
row = cursor.fetchone()
37+
38+
if row and row[3] != UserModel.split('.')[1].lower():
39+
# Drop the existing ForeignKey constraint
40+
cursor.execute(f"ALTER TABLE {table_name} DROP CONSTRAINT {row[0]}")
41+
42+
# Add the new ForeignKey constraint
43+
cursor.execute(f"""
44+
ALTER TABLE {table_name}
45+
ADD CONSTRAINT {row[0]}
46+
FOREIGN KEY (created_by_id) REFERENCES {UserModel.split('.')[0]}({UserModel.split('.')[1]});
47+
""")
648

749
class Migration(migrations.Migration):
850

@@ -21,7 +63,8 @@ class Migration(migrations.Migration):
2163
('topology', models.JSONField()),
2264
('layout_context', models.JSONField(blank=True, null=True)),
2365
('timestamp', models.DateTimeField()),
24-
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.netboxuser')),
66+
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=get_user_model())),
2567
],
2668
),
69+
migrations.RunPython(update_foreign_key),
2770
]

nextbox_ui_plugin/models.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
from django.db import models
22
from utilities.querysets import RestrictedQuerySet
3+
from django.conf import settings
4+
from packaging import version
35

6+
NETBOX_CURRENT_VERSION = version.parse(settings.VERSION)
7+
8+
def get_user_model():
9+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
10+
return 'users.User'
11+
else:
12+
return 'users.NetBoxUser'
413

514
class SavedTopology(models.Model):
615

716
name = models.CharField(max_length=100, blank=True)
817
topology = models.JSONField()
918
layout_context = models.JSONField(null=True, blank=True)
1019
created_by = models.ForeignKey(
11-
to="users.NetBoxUser",
20+
to=get_user_model(),
1221
on_delete=models.CASCADE,
1322
blank=False,
1423
null=False,

nextbox_ui_plugin/navigation.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from extras.plugins import PluginMenuItem
1+
from packaging import version
2+
from django.conf import settings
3+
NETBOX_CURRENT_VERSION = version.parse(settings.VERSION)
4+
5+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
6+
from netbox.plugins import PluginMenuItem
7+
else:
8+
from extras.plugins import PluginMenuItem
29

310

411
menu_items = (

nextbox_ui_plugin/views.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ def get_icon_type(device_id):
184184
4. Default 'undefined'
185185
"""
186186
nb_device = Device.objects.get(id=device_id)
187+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
188+
device_role_obj = nb_device.role
189+
else:
190+
device_role_obj = nb_device.device_role
187191
if not nb_device:
188192
return 'unknown'
189193
for tag in nb_device.tags.names():
@@ -194,7 +198,7 @@ def get_icon_type(device_id):
194198
if model_base in str(nb_device.device_type.model):
195199
return icon_type
196200
for role_slug, icon_type in ICON_ROLE_MAP.items():
197-
if str(nb_device.device_role.slug) == role_slug:
201+
if str(device_role_obj.slug) == role_slug:
198202
return icon_type
199203
return 'unknown'
200204

@@ -268,6 +272,10 @@ def get_vlan_topology(nb_devices_qs, vlans):
268272
for device in devices:
269273
device_is_passive = False
270274
device_url = device.get_absolute_url()
275+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
276+
device_role_obj = device.role
277+
else:
278+
device_role_obj = device.device_role
271279
primary_ip = ''
272280
if device.primary_ip:
273281
primary_ip = str(device.primary_ip.address)
@@ -281,18 +289,18 @@ def get_vlan_topology(nb_devices_qs, vlans):
281289
'primaryIP': primary_ip,
282290
'serial_number': device.serial,
283291
'model': device.device_type.model,
284-
'deviceRole': device.device_role.slug,
292+
'deviceRole': device_role_obj.slug,
285293
'layerSortPreference': get_node_layer_sort_preference(
286-
device.device_role.slug
294+
device_role_obj.slug
287295
),
288296
'icon': get_icon_type(
289297
device.id
290298
),
291299
'isPassive': device_is_passive,
292300
'tags': tags,
293301
})
294-
is_visible = not (device.device_role.slug in UNDISPLAYED_DEVICE_ROLE_SLUGS)
295-
device_roles.add((device.device_role.slug, device.device_role.name, is_visible))
302+
is_visible = not (device_role_obj.slug in UNDISPLAYED_DEVICE_ROLE_SLUGS)
303+
device_roles.add((device_role_obj.slug, device_role_obj.name, is_visible))
296304

297305
mapped_links = []
298306
for interface in filtred_interfaces:
@@ -333,6 +341,10 @@ def get_topology(nb_devices_qs):
333341
device_is_passive = False
334342
device_url = nb_device.get_absolute_url()
335343
primary_ip = ''
344+
if NETBOX_CURRENT_VERSION >= version.parse("4.0.0"):
345+
device_role_obj = nb_device.role
346+
else:
347+
device_role_obj = nb_device.device_role
336348
if nb_device.primary_ip:
337349
primary_ip = str(nb_device.primary_ip.address)
338350
tags = [str(tag) for tag in nb_device.tags.names()] or []
@@ -382,18 +394,18 @@ def get_topology(nb_devices_qs):
382394
'primaryIP': primary_ip,
383395
'serial_number': nb_device.serial,
384396
'model': nb_device.device_type.model,
385-
'deviceRole': nb_device.device_role.slug,
397+
'deviceRole': device_role_obj.slug,
386398
'layerSortPreference': get_node_layer_sort_preference(
387-
nb_device.device_role.slug
399+
device_role_obj.slug
388400
),
389401
'icon': get_icon_type(
390402
nb_device.id
391403
),
392404
'isPassive': device_is_passive,
393405
'tags': tags,
394406
})
395-
is_visible = not (nb_device.device_role.slug in UNDISPLAYED_DEVICE_ROLE_SLUGS)
396-
device_roles.add((nb_device.device_role.slug, nb_device.device_role.name, is_visible))
407+
is_visible = not (device_role_obj.slug in UNDISPLAYED_DEVICE_ROLE_SLUGS)
408+
device_roles.add((device_role_obj.slug, device_role_obj.name, is_visible))
397409
if not links_from_device:
398410
continue
399411
for link in links_from_device:

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
setup(
99
name='nextbox_ui_plugin',
10-
version='0.14.0',
10+
version='0.15.0',
1111
url='https://github.com/iDebugAll/nextbox-ui-plugin',
12-
download_url='https://github.com/iDebugAll/nextbox-ui-plugin/archive/v0.14.0.tar.gz',
12+
download_url='https://github.com/iDebugAll/nextbox-ui-plugin/archive/v0.15.0.tar.gz',
1313
description='A topology visualization plugin for Netbox powered by NextUI Toolkit.',
1414
long_description=long_description,
1515
long_description_content_type='text/markdown',

0 commit comments

Comments
 (0)