Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #377: Non-devices do not save coordinates #387

Merged
merged 9 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion netbox_topology_views/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework.serializers import ModelSerializer
from netbox.api.serializers import NetBoxModelSerializer

from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate
from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate


class TopologyDummySerializer(ModelSerializer):
Expand Down Expand Up @@ -32,6 +32,21 @@ class Meta:
model = Coordinate
fields = ("x", "y")

class CircuitCoordinateSerializer(NetBoxModelSerializer):
class Meta:
model = CircuitCoordinate
fields = ("x", "y")

class PowerPanelCoordinateSerializer(NetBoxModelSerializer):
class Meta:
model = PowerPanelCoordinate
fields = ("x", "y")

class PowerFeedCoordinateSerializer(NetBoxModelSerializer):
class Meta:
model = PowerFeedCoordinate
fields = ("x", "y")

class IndividualOptionsSerializer(NetBoxModelSerializer):
class Meta:
model = IndividualOptions
Expand Down
17 changes: 12 additions & 5 deletions netbox_topology_views/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
RoleImageSerializer,
TopologyDummySerializer,
)
from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate
import netbox_topology_views.models
from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate
from netbox_topology_views.views import get_topology_data
from netbox_topology_views.utils import get_image_from_url, export_data_to_xml, get_query_settings
from netbox_topology_views.filters import DeviceFilterSet
Expand All @@ -41,20 +42,26 @@ def save_coords(self, request):
if device_id.startswith("c"):
device_id = device_id.lstrip("c")
actual_device = Circuit.objects.get(id=device_id)
model_name = 'CircuitCoordinate'
elif device_id.startswith("p"):
device_id = device_id.lstrip("p")
actual_device = PowerPanel.objects.get(id=device_id)
model_name = 'PowerPanelCoordinate'
elif device_id.startswith("f"):
device_id = device_id.lstrip("f")
actual_device = PowerFeed.objects.get(id=device_id)
model_name = 'PowerFeedCoordinate'
elif device_id.isnumeric():
actual_device = Device.objects.get(id=device_id)
model_name = 'Coordinate'

if not actual_device:
return Response({"status": "invalid node_id in body"}, status=400)

model_class = getattr(netbox_topology_views.models, model_name)

if group_id is None or group_id == "default":
group_id = Coordinate.get_or_create_default_group(group_id)
group_id = model_class.get_or_create_default_group(group_id)
if not group_id:
return Response(
{"status": "Error while creating default group."}, status=500
Expand All @@ -66,12 +73,12 @@ def save_coords(self, request):
# Hen-and-egg-problem. Thanks, Django! By default, Django updates records that
# already exist and inserts otherwise. This does not work with our
# unique_together key if no pk is given. But: No record, no pk.
if not Coordinate.objects.filter(group=group, device=actual_device):
if not model_class.objects.filter(group=group, device=actual_device):
# Unique group/device pair does not exist. Prepare new data set
coords = Coordinate(group=group, device=actual_device, x=x_coord, y=y_coord)
coords = model_class(group=group, device=actual_device, x=x_coord, y=y_coord)
else:
# Unique group/device pair already exists. Update data
coords = Coordinate(pk=Coordinate.objects.get(group=group, device=actual_device).pk, group=group, device=actual_device, x=x_coord, y=y_coord)
coords = model_class(pk=model_class.objects.get(group=group, device=actual_device).pk, group=group, device=actual_device, x=x_coord, y=y_coord)
coords.save()
except:
return Response(
Expand Down
71 changes: 69 additions & 2 deletions netbox_topology_views/filters.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import django_filters
from circuits.models import Circuit
from dcim.choices import DeviceStatusChoices
from dcim.models import Device, DeviceRole, Location, Rack, Region, Site, SiteGroup, Manufacturer, DeviceType, Platform
from dcim.models import Device, DeviceRole, Location, Rack, Region, Site, SiteGroup, Manufacturer, DeviceType, Platform, PowerPanel, PowerFeed
from django.db.models import Q
from extras.filtersets import LocalConfigContextFilterSet
from extras.models import ConfigTemplate
from netbox.filtersets import NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter, MultiValueCharFilter, MultiValueMACAddressFilter
from netbox_topology_views.models import CoordinateGroup, Coordinate
from netbox_topology_views.models import CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate

class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet):
q = django_filters.CharFilter(
Expand Down Expand Up @@ -164,6 +165,72 @@ def _has_oob_ip(self, queryset, name, value):
def _virtual_chassis_member(self, queryset, name, value):
return queryset.exclude(virtual_chassis__isnull=value)

class CircuitCoordinatesFilterSet(NetBoxModelFilterSet):
group = django_filters.ModelMultipleChoiceFilter(
queryset = CoordinateGroup.objects.all(),
)

device = django_filters.ModelMultipleChoiceFilter(
queryset = Circuit.objects.all(),
)

class Meta:
model = CircuitCoordinate
fields = ['id', 'group', 'device', 'x', 'y']

def search(self, queryset, name, value):
"""Perform the filtered search."""
if not value.strip():
return queryset
return queryset.filter(
Q(group__name__icontains=value) |
Q(device__name__icontains=value)
)

class PowerPanelCoordinatesFilterSet(NetBoxModelFilterSet):
group = django_filters.ModelMultipleChoiceFilter(
queryset = CoordinateGroup.objects.all(),
)

device = django_filters.ModelMultipleChoiceFilter(
queryset = PowerPanel.objects.all(),
)

class Meta:
model = PowerPanelCoordinate
fields = ['id', 'group', 'device', 'x', 'y']

def search(self, queryset, name, value):
"""Perform the filtered search."""
if not value.strip():
return queryset
return queryset.filter(
Q(group__name__icontains=value) |
Q(device__name__icontains=value)
)

class PowerFeedCoordinatesFilterSet(NetBoxModelFilterSet):
group = django_filters.ModelMultipleChoiceFilter(
queryset = CoordinateGroup.objects.all(),
)

device = django_filters.ModelMultipleChoiceFilter(
queryset = PowerFeed.objects.all(),
)

class Meta:
model = PowerFeedCoordinate
fields = ['id', 'group', 'device', 'x', 'y']

def search(self, queryset, name, value):
"""Perform the filtered search."""
if not value.strip():
return queryset
return queryset.filter(
Q(group__name__icontains=value) |
Q(device__name__icontains=value)
)

class CoordinatesFilterSet(NetBoxModelFilterSet):
group = django_filters.ModelMultipleChoiceFilter(
queryset = CoordinateGroup.objects.all(),
Expand Down
122 changes: 120 additions & 2 deletions netbox_topology_views/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from django.utils.translation import gettext as _

from dcim.models import Device, Site, SiteGroup, Region, DeviceRole, Location, Rack, Manufacturer, DeviceType, Platform
from circuits.models import Circuit
from dcim.models import Device, Site, SiteGroup, Region, DeviceRole, Location, Rack, Manufacturer, DeviceType, Platform, PowerPanel, PowerFeed

from django import forms
from dcim.choices import DeviceStatusChoices, DeviceAirflowChoices
Expand All @@ -21,7 +22,7 @@
DynamicModelMultipleChoiceField
)

from netbox_topology_views.models import IndividualOptions, CoordinateGroup, Coordinate
from netbox_topology_views.models import IndividualOptions, CoordinateGroup, Coordinate, CircuitCoordinate, PowerPanelCoordinate, PowerFeedCoordinate

class DeviceFilterForm(
LocalConfigContextFilterForm,
Expand Down Expand Up @@ -272,6 +273,33 @@ class Meta:
model = CoordinateGroup
fields = ('name', 'description')

class CircuitCoordinatesForm(NetBoxModelForm):
fieldsets = (
('CircuitCoordinate', ('group', 'device', 'x', 'y')),
)

class Meta:
model = CircuitCoordinate
fields = ('group', 'device', 'x', 'y')

class PowerPanelCoordinatesForm(NetBoxModelForm):
fieldsets = (
('PowerPanel', ('group', 'device', 'x', 'y')),
)

class Meta:
model = PowerPanelCoordinate
fields = ('group', 'device', 'x', 'y')

class PowerFeedCoordinatesForm(NetBoxModelForm):
fieldsets = (
('PowerFeedCoordinate', ('group', 'device', 'x', 'y')),
)

class Meta:
model = PowerFeedCoordinate
fields = ('group', 'device', 'x', 'y')

class CoordinatesForm(NetBoxModelForm):
fieldsets = (
('Coordinate', ('group', 'device', 'x', 'y')),
Expand All @@ -281,11 +309,101 @@ class Meta:
model = Coordinate
fields = ('group', 'device', 'x', 'y')

class CircuitCoordinatesImportForm(NetBoxModelImportForm):
class Meta:
model = CircuitCoordinate
fields = ('group', 'device', 'x', 'y')

class PowerPanelCoordinatesImportForm(NetBoxModelImportForm):
class Meta:
model = PowerPanelCoordinate
fields = ('group', 'device', 'x', 'y')

class PowerFeedCoordinatesImportForm(NetBoxModelImportForm):
class Meta:
model = PowerFeedCoordinate
fields = ('group', 'device', 'x', 'y')

class CoordinatesImportForm(NetBoxModelImportForm):
class Meta:
model = Coordinate
fields = ('group', 'device', 'x', 'y')

class CircuitCoordinatesFilterForm(NetBoxModelFilterSetForm):
model = CircuitCoordinate
fieldsets = (
(None, ('q', 'filter_id')),
('CircuitCoordinates', ('group', 'device', 'x', 'y'))
)

group = forms.ModelMultipleChoiceField(
queryset=CoordinateGroup.objects.all(),
required=False
)

device = DynamicModelMultipleChoiceField(
queryset=Circuit.objects.all(),
required=False
)

x = forms.IntegerField(
required=False
)

y = forms.IntegerField(
required=False
)

class PowerPanelCoordinatesFilterForm(NetBoxModelFilterSetForm):
model = PowerPanelCoordinate
fieldsets = (
(None, ('q', 'filter_id')),
('PowerPanelCoordinates', ('group', 'device', 'x', 'y'))
)

group = forms.ModelMultipleChoiceField(
queryset=CoordinateGroup.objects.all(),
required=False
)

device = DynamicModelMultipleChoiceField(
queryset=PowerPanel.objects.all(),
required=False
)

x = forms.IntegerField(
required=False
)

y = forms.IntegerField(
required=False
)

class PowerFeedCoordinatesFilterForm(NetBoxModelFilterSetForm):
model = Coordinate
fieldsets = (
(None, ('q', 'filter_id')),
('PowerFeedCoordinates', ('group', 'device', 'x', 'y'))
)

group = forms.ModelMultipleChoiceField(
queryset=CoordinateGroup.objects.all(),
required=False
)

device = DynamicModelMultipleChoiceField(
queryset=PowerFeed.objects.all(),
required=False
)

x = forms.IntegerField(
required=False
)

y = forms.IntegerField(
required=False
)

class CoordinatesFilterForm(NetBoxModelFilterSetForm):
model = Coordinate
fieldsets = (
Expand Down
Loading