mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Merged v2.5.13
This commit is contained in:
27
CHANGELOG.md
27
CHANGELOG.md
@ -213,6 +213,33 @@ functionality provided by the front end UI.
|
||||
|
||||
---
|
||||
|
||||
2.5.13 (2019-05-31)
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#2813](https://github.com/digitalocean/netbox/issues/2813) - Add tenant group filters
|
||||
* [#3085](https://github.com/digitalocean/netbox/issues/3085) - Catch all exceptions during export template rendering
|
||||
* [#3138](https://github.com/digitalocean/netbox/issues/3138) - Add 2.5GE and 5GE interface form factors
|
||||
* [#3151](https://github.com/digitalocean/netbox/issues/3151) - Add inventory item count to manufacturers list
|
||||
* [#3156](https://github.com/digitalocean/netbox/issues/3156) - Add site link to rack reservations overview
|
||||
* [#3183](https://github.com/digitalocean/netbox/issues/3183) - Enable bulk deletion of sites
|
||||
* [#3185](https://github.com/digitalocean/netbox/issues/3185) - Improve performance for custom field access within templates
|
||||
* [#3186](https://github.com/digitalocean/netbox/issues/3186) - Add interface name filter for IP addresses
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#3031](https://github.com/digitalocean/netbox/issues/3031) - Fixed form field population of tags with spaces
|
||||
* [#3132](https://github.com/digitalocean/netbox/issues/3132) - Circuit termination missing from available cable termination types
|
||||
* [#3150](https://github.com/digitalocean/netbox/issues/3150) - Fix formatting of cable length during cable trace
|
||||
* [#3184](https://github.com/digitalocean/netbox/issues/3184) - Correctly display color block for white cables
|
||||
* [#3190](https://github.com/digitalocean/netbox/issues/3190) - Fix custom field rendering for Jinja2 export templates
|
||||
* [#3211](https://github.com/digitalocean/netbox/issues/3211) - Fix error handling when attempting to delete a protected object via API
|
||||
* [#3223](https://github.com/digitalocean/netbox/issues/3223) - Fix filtering devices by "has power outlets"
|
||||
* [#3227](https://github.com/digitalocean/netbox/issues/3227) - Fix exception when deleting a circuit with a termination(s)
|
||||
* [#3228](https://github.com/digitalocean/netbox/issues/3228) - Fixed login link retaining query parameters
|
||||
|
||||
---
|
||||
|
||||
2.5.12 (2019-05-01)
|
||||
|
||||
## Bug Fixes
|
||||
|
@ -3,7 +3,7 @@ from django.db.models import Q
|
||||
|
||||
from dcim.models import Site
|
||||
from extras.filters import CustomFieldFilterSet
|
||||
from tenancy.models import Tenant
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
|
||||
from .constants import CIRCUIT_STATUS_CHOICES
|
||||
from .models import Provider, Circuit, CircuitTermination, CircuitType
|
||||
@ -54,7 +54,7 @@ class CircuitTypeFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class CircuitFilter(CustomFieldFilterSet):
|
||||
class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -87,16 +87,6 @@ class CircuitFilter(CustomFieldFilterSet):
|
||||
choices=CIRCUIT_STATUS_CHOICES,
|
||||
null_value=None
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='terminations__site',
|
||||
queryset=Site.objects.all(),
|
||||
|
@ -4,6 +4,7 @@ from taggit.forms import TagField
|
||||
from dcim.models import Site
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField,
|
||||
@ -265,8 +266,9 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
||||
]
|
||||
|
||||
|
||||
class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = Circuit
|
||||
field_order = ['q', 'type', 'provider', 'status', 'site', 'tenant_group', 'tenant', 'commit_rate']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -292,16 +294,6 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
required=False,
|
||||
widget=StaticSelect2Multiple()
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
|
@ -274,11 +274,16 @@ class CircuitTermination(CableTermination):
|
||||
"""
|
||||
Reference the parent circuit when recording the change.
|
||||
"""
|
||||
try:
|
||||
related_object = self.circuit
|
||||
except Circuit.DoesNotExist:
|
||||
# Parent circuit has been deleted
|
||||
related_object = None
|
||||
ObjectChange(
|
||||
user=user,
|
||||
request_id=request_id,
|
||||
changed_object=self,
|
||||
related_object=self.circuit,
|
||||
related_object=related_object,
|
||||
action=action,
|
||||
object_data=serialize_object(self)
|
||||
).save()
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from dcim.views import CableCreateView, CableTraceView
|
||||
from extras.views import ObjectChangeLogView
|
||||
@ -9,41 +9,42 @@ app_name = 'circuits'
|
||||
urlpatterns = [
|
||||
|
||||
# Providers
|
||||
url(r'^providers/$', views.ProviderListView.as_view(), name='provider_list'),
|
||||
url(r'^providers/add/$', views.ProviderCreateView.as_view(), name='provider_add'),
|
||||
url(r'^providers/import/$', views.ProviderBulkImportView.as_view(), name='provider_import'),
|
||||
url(r'^providers/edit/$', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
|
||||
url(r'^providers/delete/$', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
|
||||
url(r'^providers/(?P<slug>[\w-]+)/$', views.ProviderView.as_view(), name='provider'),
|
||||
url(r'^providers/(?P<slug>[\w-]+)/edit/$', views.ProviderEditView.as_view(), name='provider_edit'),
|
||||
url(r'^providers/(?P<slug>[\w-]+)/delete/$', views.ProviderDeleteView.as_view(), name='provider_delete'),
|
||||
url(r'^providers/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
||||
path(r'providers/', views.ProviderListView.as_view(), name='provider_list'),
|
||||
path(r'providers/add/', views.ProviderCreateView.as_view(), name='provider_add'),
|
||||
path(r'providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'),
|
||||
path(r'providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
|
||||
path(r'providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
|
||||
path(r'providers/<slug:slug>/', views.ProviderView.as_view(), name='provider'),
|
||||
path(r'providers/<slug:slug>/edit/', views.ProviderEditView.as_view(), name='provider_edit'),
|
||||
path(r'providers/<slug:slug>/delete/', views.ProviderDeleteView.as_view(), name='provider_delete'),
|
||||
path(r'providers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
||||
|
||||
# Circuit types
|
||||
url(r'^circuit-types/$', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||
url(r'^circuit-types/add/$', views.CircuitTypeCreateView.as_view(), name='circuittype_add'),
|
||||
url(r'^circuit-types/import/$', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
||||
url(r'^circuit-types/delete/$', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
||||
url(r'^circuit-types/(?P<slug>[\w-]+)/edit/$', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
||||
url(r'^circuit-types/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
|
||||
path(r'circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||
path(r'circuit-types/add/', views.CircuitTypeCreateView.as_view(), name='circuittype_add'),
|
||||
path(r'circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
||||
path(r'circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
||||
path(r'circuit-types/<slug:slug>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
||||
path(r'circuit-types/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
|
||||
|
||||
# Circuits
|
||||
url(r'^circuits/$', views.CircuitListView.as_view(), name='circuit_list'),
|
||||
url(r'^circuits/add/$', views.CircuitCreateView.as_view(), name='circuit_add'),
|
||||
url(r'^circuits/import/$', views.CircuitBulkImportView.as_view(), name='circuit_import'),
|
||||
url(r'^circuits/edit/$', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
|
||||
url(r'^circuits/delete/$', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
|
||||
url(r'^circuits/(?P<pk>\d+)/$', views.CircuitView.as_view(), name='circuit'),
|
||||
url(r'^circuits/(?P<pk>\d+)/edit/$', views.CircuitEditView.as_view(), name='circuit_edit'),
|
||||
url(r'^circuits/(?P<pk>\d+)/delete/$', views.CircuitDeleteView.as_view(), name='circuit_delete'),
|
||||
url(r'^circuits/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
|
||||
url(r'^circuits/(?P<pk>\d+)/terminations/swap/$', views.circuit_terminations_swap, name='circuit_terminations_swap'),
|
||||
path(r'circuits/', views.CircuitListView.as_view(), name='circuit_list'),
|
||||
path(r'circuits/add/', views.CircuitCreateView.as_view(), name='circuit_add'),
|
||||
path(r'circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'),
|
||||
path(r'circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
|
||||
path(r'circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
|
||||
path(r'circuits/<int:pk>/', views.CircuitView.as_view(), name='circuit'),
|
||||
path(r'circuits/<int:pk>/edit/', views.CircuitEditView.as_view(), name='circuit_edit'),
|
||||
path(r'circuits/<int:pk>/delete/', views.CircuitDeleteView.as_view(), name='circuit_delete'),
|
||||
path(r'circuits/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
|
||||
path(r'circuits/<int:pk>/terminations/swap/', views.circuit_terminations_swap, name='circuit_terminations_swap'),
|
||||
|
||||
# Circuit terminations
|
||||
url(r'^circuits/(?P<circuit>\d+)/terminations/add/$', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
|
||||
url(r'^circuit-terminations/(?P<pk>\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
|
||||
url(r'^circuit-terminations/(?P<pk>\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
|
||||
url(r'^circuit-terminations/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
|
||||
url(r'^circuit-terminations/(?P<pk>\d+)/trace/$', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
|
||||
|
||||
path(r'circuits/<int:circuit>/terminations/add/', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
|
||||
path(r'circuit-terminations/<int:pk>/edit/', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
|
||||
path(r'circuit-terminations/<int:pk>/delete/', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
|
||||
path(r'circuit-terminations/<int:termination_a_id>/connect/<str:termination_b_type>/', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
|
||||
path(r'circuit-terminations/<int:pk>/trace/', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
|
||||
|
||||
]
|
||||
|
@ -75,6 +75,8 @@ IFACE_TYPE_100ME_FIXED = 800
|
||||
IFACE_TYPE_1GE_FIXED = 1000
|
||||
IFACE_TYPE_1GE_GBIC = 1050
|
||||
IFACE_TYPE_1GE_SFP = 1100
|
||||
IFACE_TYPE_2GE_FIXED = 1120
|
||||
IFACE_TYPE_5GE_FIXED = 1130
|
||||
IFACE_TYPE_10GE_FIXED = 1150
|
||||
IFACE_TYPE_10GE_CX4 = 1170
|
||||
IFACE_TYPE_10GE_SFP_PLUS = 1200
|
||||
@ -150,6 +152,8 @@ IFACE_TYPE_CHOICES = [
|
||||
[
|
||||
[IFACE_TYPE_100ME_FIXED, '100BASE-TX (10/100ME)'],
|
||||
[IFACE_TYPE_1GE_FIXED, '1000BASE-T (1GE)'],
|
||||
[IFACE_TYPE_2GE_FIXED, '2.5GBASE-T (2.5GE)'],
|
||||
[IFACE_TYPE_5GE_FIXED, '5GBASE-T (5GE)'],
|
||||
[IFACE_TYPE_10GE_FIXED, '10GBASE-T (10GE)'],
|
||||
[IFACE_TYPE_10GE_CX4, '10GBASE-CX4 (10GE)'],
|
||||
]
|
||||
@ -360,7 +364,7 @@ CONNECTION_STATUS_CHOICES = [
|
||||
|
||||
# Cable endpoint types
|
||||
CABLE_TERMINATION_TYPES = [
|
||||
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport',
|
||||
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination',
|
||||
]
|
||||
|
||||
# Cable types
|
||||
|
@ -6,6 +6,7 @@ from netaddr import EUI
|
||||
from netaddr.core import AddrFormatError
|
||||
|
||||
from extras.filters import CustomFieldFilterSet
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from tenancy.models import Tenant
|
||||
from utilities.constants import COLOR_CHOICES
|
||||
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
|
||||
@ -37,7 +38,7 @@ class RegionFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class SiteFilter(CustomFieldFilterSet):
|
||||
class SiteFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -61,16 +62,6 @@ class SiteFilter(CustomFieldFilterSet):
|
||||
to_field_name='slug',
|
||||
label='Region (slug)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
@ -125,7 +116,7 @@ class RackRoleFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug', 'color']
|
||||
|
||||
|
||||
class RackFilter(CustomFieldFilterSet):
|
||||
class RackFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -154,16 +145,6 @@ class RackFilter(CustomFieldFilterSet):
|
||||
to_field_name='slug',
|
||||
label='Group',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=RACK_STATUS_CHOICES,
|
||||
null_value=None
|
||||
@ -199,7 +180,7 @@ class RackFilter(CustomFieldFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class RackReservationFilter(django_filters.FilterSet):
|
||||
class RackReservationFilter(TenancyFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -234,16 +215,6 @@ class RackReservationFilter(django_filters.FilterSet):
|
||||
to_field_name='slug',
|
||||
label='Group',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
user_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=User.objects.all(),
|
||||
label='User (ID)',
|
||||
@ -449,7 +420,7 @@ class PlatformFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug', 'napalm_driver']
|
||||
|
||||
|
||||
class DeviceFilter(CustomFieldFilterSet):
|
||||
class DeviceFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -484,16 +455,6 @@ class DeviceFilter(CustomFieldFilterSet):
|
||||
to_field_name='slug',
|
||||
label='Role (slug)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
platform_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Platform.objects.all(),
|
||||
label='Platform (ID)',
|
||||
@ -639,7 +600,7 @@ class DeviceFilter(CustomFieldFilterSet):
|
||||
return queryset.exclude(powerports__isnull=value)
|
||||
|
||||
def _power_outlets(self, queryset, name, value):
|
||||
return queryset.exclude(poweroutlets_isnull=value)
|
||||
return queryset.exclude(poweroutlets__isnull=value)
|
||||
|
||||
def _interfaces(self, queryset, name, value):
|
||||
return queryset.exclude(interfaces__isnull=value)
|
||||
|
@ -14,7 +14,8 @@ from circuits.models import Circuit, Provider
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||
from ipam.models import IPAddress, VLAN, VLANGroup
|
||||
from tenancy.forms import TenancyForm
|
||||
from tenancy.models import Tenant
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from utilities.forms import (
|
||||
APISelect, APISelectMultiple, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm,
|
||||
BulkEditNullBooleanSelect, ChainedFieldsMixin, ChainedModelChoiceField, ColorSelect, CommentField, ComponentForm,
|
||||
@ -256,8 +257,9 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
||||
]
|
||||
|
||||
|
||||
class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = Site
|
||||
field_order = ['q', 'status', 'region', 'tenant_group', 'tenant']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -276,16 +278,6 @@ class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
@ -596,8 +588,9 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
||||
]
|
||||
|
||||
|
||||
class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = Rack
|
||||
field_order = ['q', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -619,16 +612,6 @@ class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
choices=RACK_STATUS_CHOICES,
|
||||
required=False,
|
||||
@ -689,40 +672,6 @@ class RackReservationForm(BootstrapMixin, TenancyForm, forms.ModelForm):
|
||||
return unit_choices
|
||||
|
||||
|
||||
class RackReservationFilterForm(BootstrapMixin, forms.Form):
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/sites/",
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
group_id = FilterChoiceField(
|
||||
queryset=RackGroup.objects.select_related('site'),
|
||||
label='Rack group',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/rack-groups/",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RackReservationBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=RackReservation.objects.all(),
|
||||
@ -751,6 +700,31 @@ class RackReservationBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
nullable_fields = []
|
||||
|
||||
|
||||
class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm):
|
||||
field_order = ['q', 'site', 'group_id', 'tenant_group', 'tenant']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/sites/",
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
group_id = FilterChoiceField(
|
||||
queryset=RackGroup.objects.select_related('site'),
|
||||
label='Rack group',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/rack-groups/",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Manufacturers
|
||||
#
|
||||
@ -1656,8 +1630,12 @@ class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
||||
]
|
||||
|
||||
|
||||
class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class DeviceFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = Device
|
||||
field_order = [
|
||||
'q', 'region', 'site', 'rack_group_id', 'rack_id', 'status', 'role', 'tenant_group', 'tenant',
|
||||
'manufacturer_id', 'device_type_id', 'mac_address', 'has_primary_ip',
|
||||
]
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -1715,16 +1693,6 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
manufacturer_id = FilterChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
label='Manufacturer',
|
||||
@ -3368,11 +3336,33 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
site = FilterChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='slug',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/sites/",
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
tenant_group = FilterChoiceField(
|
||||
queryset=TenantGroup.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenant-groups/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
filter_for={
|
||||
'tenant': 'group'
|
||||
}
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -314,7 +314,12 @@ class RackDetailTable(RackTable):
|
||||
|
||||
class RackReservationTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
|
||||
site = tables.LinkColumn(
|
||||
viewname='dcim:site',
|
||||
accessor=Accessor('rack.site'),
|
||||
args=[Accessor('rack.site.slug')],
|
||||
)
|
||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
||||
unit_list = tables.Column(orderable=False, verbose_name='Units')
|
||||
actions = tables.TemplateColumn(
|
||||
@ -323,7 +328,7 @@ class RackReservationTable(BaseTable):
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = RackReservation
|
||||
fields = ('pk', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions')
|
||||
fields = ('pk', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions')
|
||||
|
||||
|
||||
#
|
||||
@ -332,16 +337,26 @@ class RackReservationTable(BaseTable):
|
||||
|
||||
class ManufacturerTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn(verbose_name='Name')
|
||||
devicetype_count = tables.Column(verbose_name='Device Types')
|
||||
platform_count = tables.Column(verbose_name='Platforms')
|
||||
slug = tables.Column(verbose_name='Slug')
|
||||
actions = tables.TemplateColumn(template_code=MANUFACTURER_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
|
||||
verbose_name='')
|
||||
name = tables.LinkColumn()
|
||||
devicetype_count = tables.Column(
|
||||
verbose_name='Device Types'
|
||||
)
|
||||
inventoryitem_count = tables.Column(
|
||||
verbose_name='Inventory Items'
|
||||
)
|
||||
platform_count = tables.Column(
|
||||
verbose_name='Platforms'
|
||||
)
|
||||
slug = tables.Column()
|
||||
actions = tables.TemplateColumn(
|
||||
template_code=MANUFACTURER_ACTIONS,
|
||||
attrs={'td': {'class': 'text-right noprint'}},
|
||||
verbose_name=''
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Manufacturer
|
||||
fields = ('pk', 'name', 'devicetype_count', 'platform_count', 'slug', 'actions')
|
||||
fields = ('pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'slug', 'actions')
|
||||
|
||||
|
||||
#
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from extras.views import ObjectChangeLogView, ImageAttachmentEditView
|
||||
from ipam.views import ServiceCreateView
|
||||
@ -14,293 +14,294 @@ app_name = 'dcim'
|
||||
urlpatterns = [
|
||||
|
||||
# Regions
|
||||
url(r'^regions/$', views.RegionListView.as_view(), name='region_list'),
|
||||
url(r'^regions/add/$', views.RegionCreateView.as_view(), name='region_add'),
|
||||
url(r'^regions/import/$', views.RegionBulkImportView.as_view(), name='region_import'),
|
||||
url(r'^regions/delete/$', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
|
||||
url(r'^regions/(?P<pk>\d+)/edit/$', views.RegionEditView.as_view(), name='region_edit'),
|
||||
url(r'^regions/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
||||
path(r'regions/', views.RegionListView.as_view(), name='region_list'),
|
||||
path(r'regions/add/', views.RegionCreateView.as_view(), name='region_add'),
|
||||
path(r'regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
|
||||
path(r'regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
|
||||
path(r'regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
|
||||
path(r'regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
||||
|
||||
# Sites
|
||||
url(r'^sites/$', views.SiteListView.as_view(), name='site_list'),
|
||||
url(r'^sites/add/$', views.SiteCreateView.as_view(), name='site_add'),
|
||||
url(r'^sites/import/$', views.SiteBulkImportView.as_view(), name='site_import'),
|
||||
url(r'^sites/edit/$', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
|
||||
url(r'^sites/(?P<slug>[\w-]+)/$', views.SiteView.as_view(), name='site'),
|
||||
url(r'^sites/(?P<slug>[\w-]+)/edit/$', views.SiteEditView.as_view(), name='site_edit'),
|
||||
url(r'^sites/(?P<slug>[\w-]+)/delete/$', views.SiteDeleteView.as_view(), name='site_delete'),
|
||||
url(r'^sites/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}),
|
||||
url(r'^sites/(?P<object_id>\d+)/images/add/$', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}),
|
||||
path(r'sites/', views.SiteListView.as_view(), name='site_list'),
|
||||
path(r'sites/add/', views.SiteCreateView.as_view(), name='site_add'),
|
||||
path(r'sites/import/', views.SiteBulkImportView.as_view(), name='site_import'),
|
||||
path(r'sites/edit/', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
|
||||
path(r'sites/delete/', views.SiteBulkDeleteView.as_view(), name='site_bulk_delete'),
|
||||
path(r'sites/<slug:slug>/', views.SiteView.as_view(), name='site'),
|
||||
path(r'sites/<slug:slug>/edit/', views.SiteEditView.as_view(), name='site_edit'),
|
||||
path(r'sites/<slug:slug>/delete/', views.SiteDeleteView.as_view(), name='site_delete'),
|
||||
path(r'sites/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}),
|
||||
path(r'sites/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}),
|
||||
|
||||
# Rack groups
|
||||
url(r'^rack-groups/$', views.RackGroupListView.as_view(), name='rackgroup_list'),
|
||||
url(r'^rack-groups/add/$', views.RackGroupCreateView.as_view(), name='rackgroup_add'),
|
||||
url(r'^rack-groups/import/$', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'),
|
||||
url(r'^rack-groups/delete/$', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
|
||||
url(r'^rack-groups/(?P<pk>\d+)/edit/$', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
|
||||
url(r'^rack-groups/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rackgroup_changelog', kwargs={'model': RackGroup}),
|
||||
path(r'rack-groups/', views.RackGroupListView.as_view(), name='rackgroup_list'),
|
||||
path(r'rack-groups/add/', views.RackGroupCreateView.as_view(), name='rackgroup_add'),
|
||||
path(r'rack-groups/import/', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'),
|
||||
path(r'rack-groups/delete/', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
|
||||
path(r'rack-groups/<int:pk>/edit/', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
|
||||
path(r'rack-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackgroup_changelog', kwargs={'model': RackGroup}),
|
||||
|
||||
# Rack roles
|
||||
url(r'^rack-roles/$', views.RackRoleListView.as_view(), name='rackrole_list'),
|
||||
url(r'^rack-roles/add/$', views.RackRoleCreateView.as_view(), name='rackrole_add'),
|
||||
url(r'^rack-roles/import/$', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
|
||||
url(r'^rack-roles/delete/$', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
|
||||
url(r'^rack-roles/(?P<pk>\d+)/edit/$', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
||||
url(r'^rack-roles/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
|
||||
path(r'rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'),
|
||||
path(r'rack-roles/add/', views.RackRoleCreateView.as_view(), name='rackrole_add'),
|
||||
path(r'rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
|
||||
path(r'rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
|
||||
path(r'rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
||||
path(r'rack-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
|
||||
|
||||
# Rack reservations
|
||||
url(r'^rack-reservations/$', views.RackReservationListView.as_view(), name='rackreservation_list'),
|
||||
url(r'^rack-reservations/edit/$', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
|
||||
url(r'^rack-reservations/delete/$', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
|
||||
url(r'^rack-reservations/(?P<pk>\d+)/edit/$', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
|
||||
url(r'^rack-reservations/(?P<pk>\d+)/delete/$', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
|
||||
url(r'^rack-reservations/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rackreservation_changelog', kwargs={'model': RackReservation}),
|
||||
path(r'rack-reservations/', views.RackReservationListView.as_view(), name='rackreservation_list'),
|
||||
path(r'rack-reservations/edit/', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
|
||||
path(r'rack-reservations/delete/', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
|
||||
path(r'rack-reservations/<int:pk>/edit/', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
|
||||
path(r'rack-reservations/<int:pk>/delete/', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
|
||||
path(r'rack-reservations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackreservation_changelog', kwargs={'model': RackReservation}),
|
||||
|
||||
# Racks
|
||||
url(r'^racks/$', views.RackListView.as_view(), name='rack_list'),
|
||||
url(r'^rack-elevations/$', views.RackElevationListView.as_view(), name='rack_elevation_list'),
|
||||
url(r'^racks/add/$', views.RackEditView.as_view(), name='rack_add'),
|
||||
url(r'^racks/import/$', views.RackBulkImportView.as_view(), name='rack_import'),
|
||||
url(r'^racks/edit/$', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
|
||||
url(r'^racks/delete/$', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
|
||||
url(r'^racks/(?P<pk>\d+)/$', views.RackView.as_view(), name='rack'),
|
||||
url(r'^racks/(?P<pk>\d+)/edit/$', views.RackEditView.as_view(), name='rack_edit'),
|
||||
url(r'^racks/(?P<pk>\d+)/delete/$', views.RackDeleteView.as_view(), name='rack_delete'),
|
||||
url(r'^racks/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}),
|
||||
url(r'^racks/(?P<rack>\d+)/reservations/add/$', views.RackReservationCreateView.as_view(), name='rack_add_reservation'),
|
||||
url(r'^racks/(?P<object_id>\d+)/images/add/$', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}),
|
||||
path(r'racks/', views.RackListView.as_view(), name='rack_list'),
|
||||
path(r'rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'),
|
||||
path(r'racks/add/', views.RackEditView.as_view(), name='rack_add'),
|
||||
path(r'racks/import/', views.RackBulkImportView.as_view(), name='rack_import'),
|
||||
path(r'racks/edit/', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
|
||||
path(r'racks/delete/', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
|
||||
path(r'racks/<int:pk>/', views.RackView.as_view(), name='rack'),
|
||||
path(r'racks/<int:pk>/edit/', views.RackEditView.as_view(), name='rack_edit'),
|
||||
path(r'racks/<int:pk>/delete/', views.RackDeleteView.as_view(), name='rack_delete'),
|
||||
path(r'racks/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}),
|
||||
path(r'racks/<int:rack>/reservations/add/', views.RackReservationCreateView.as_view(), name='rack_add_reservation'),
|
||||
path(r'racks/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}),
|
||||
|
||||
# Manufacturers
|
||||
url(r'^manufacturers/$', views.ManufacturerListView.as_view(), name='manufacturer_list'),
|
||||
url(r'^manufacturers/add/$', views.ManufacturerCreateView.as_view(), name='manufacturer_add'),
|
||||
url(r'^manufacturers/import/$', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
|
||||
url(r'^manufacturers/delete/$', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
|
||||
url(r'^manufacturers/(?P<slug>[\w-]+)/edit/$', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
|
||||
url(r'^manufacturers/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
|
||||
path(r'manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'),
|
||||
path(r'manufacturers/add/', views.ManufacturerCreateView.as_view(), name='manufacturer_add'),
|
||||
path(r'manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
|
||||
path(r'manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
|
||||
path(r'manufacturers/<slug:slug>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
|
||||
path(r'manufacturers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
|
||||
|
||||
# Device types
|
||||
url(r'^device-types/$', views.DeviceTypeListView.as_view(), name='devicetype_list'),
|
||||
url(r'^device-types/add/$', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
|
||||
url(r'^device-types/import/$', views.DeviceTypeBulkImportView.as_view(), name='devicetype_import'),
|
||||
url(r'^device-types/edit/$', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
|
||||
url(r'^device-types/delete/$', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
|
||||
url(r'^device-types/(?P<pk>\d+)/$', views.DeviceTypeView.as_view(), name='devicetype'),
|
||||
url(r'^device-types/(?P<pk>\d+)/edit/$', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
|
||||
url(r'^device-types/(?P<pk>\d+)/delete/$', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
|
||||
url(r'^device-types/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='devicetype_changelog', kwargs={'model': DeviceType}),
|
||||
path(r'device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'),
|
||||
path(r'device-types/add/', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
|
||||
path(r'device-types/import/', views.DeviceTypeBulkImportView.as_view(), name='devicetype_import'),
|
||||
path(r'device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
|
||||
path(r'device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
|
||||
path(r'device-types/<int:pk>/', views.DeviceTypeView.as_view(), name='devicetype'),
|
||||
path(r'device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
|
||||
path(r'device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
|
||||
path(r'device-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='devicetype_changelog', kwargs={'model': DeviceType}),
|
||||
|
||||
# Console port templates
|
||||
url(r'^device-types/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortTemplateCreateView.as_view(), name='devicetype_add_consoleport'),
|
||||
url(r'^device-types/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleport'),
|
||||
path(r'device-types/<int:pk>/console-ports/add/', views.ConsolePortTemplateCreateView.as_view(), name='devicetype_add_consoleport'),
|
||||
path(r'device-types/<int:pk>/console-ports/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleport'),
|
||||
|
||||
# Console server port templates
|
||||
url(r'^device-types/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortTemplateCreateView.as_view(), name='devicetype_add_consoleserverport'),
|
||||
url(r'^device-types/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleserverport'),
|
||||
path(r'device-types/<int:pk>/console-server-ports/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='devicetype_add_consoleserverport'),
|
||||
path(r'device-types/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleserverport'),
|
||||
|
||||
# Power port templates
|
||||
url(r'^device-types/(?P<pk>\d+)/power-ports/add/$', views.PowerPortTemplateCreateView.as_view(), name='devicetype_add_powerport'),
|
||||
url(r'^device-types/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_powerport'),
|
||||
path(r'device-types/<int:pk>/power-ports/add/', views.PowerPortTemplateCreateView.as_view(), name='devicetype_add_powerport'),
|
||||
path(r'device-types/<int:pk>/power-ports/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_powerport'),
|
||||
|
||||
# Power outlet templates
|
||||
url(r'^device-types/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletTemplateCreateView.as_view(), name='devicetype_add_poweroutlet'),
|
||||
url(r'^device-types/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletTemplateBulkDeleteView.as_view(), name='devicetype_delete_poweroutlet'),
|
||||
path(r'device-types/<int:pk>/power-outlets/add/', views.PowerOutletTemplateCreateView.as_view(), name='devicetype_add_poweroutlet'),
|
||||
path(r'device-types/<int:pk>/power-outlets/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='devicetype_delete_poweroutlet'),
|
||||
|
||||
# Interface templates
|
||||
url(r'^device-types/(?P<pk>\d+)/interfaces/add/$', views.InterfaceTemplateCreateView.as_view(), name='devicetype_add_interface'),
|
||||
url(r'^device-types/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceTemplateBulkEditView.as_view(), name='devicetype_bulkedit_interface'),
|
||||
url(r'^device-types/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceTemplateBulkDeleteView.as_view(), name='devicetype_delete_interface'),
|
||||
path(r'device-types/<int:pk>/interfaces/add/', views.InterfaceTemplateCreateView.as_view(), name='devicetype_add_interface'),
|
||||
path(r'device-types/<int:pk>/interfaces/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='devicetype_bulkedit_interface'),
|
||||
path(r'device-types/<int:pk>/interfaces/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='devicetype_delete_interface'),
|
||||
|
||||
# Front port templates
|
||||
url(r'^device-types/(?P<pk>\d+)/front-ports/add/$', views.FrontPortTemplateCreateView.as_view(), name='devicetype_add_frontport'),
|
||||
url(r'^device-types/(?P<pk>\d+)/front-ports/delete/$', views.FrontPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_frontport'),
|
||||
path(r'device-types/<int:pk>/front-ports/add/', views.FrontPortTemplateCreateView.as_view(), name='devicetype_add_frontport'),
|
||||
path(r'device-types/<int:pk>/front-ports/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_frontport'),
|
||||
|
||||
# Rear port templates
|
||||
url(r'^device-types/(?P<pk>\d+)/rear-ports/add/$', views.RearPortTemplateCreateView.as_view(), name='devicetype_add_rearport'),
|
||||
url(r'^device-types/(?P<pk>\d+)/rear-ports/delete/$', views.RearPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_rearport'),
|
||||
path(r'device-types/<int:pk>/rear-ports/add/', views.RearPortTemplateCreateView.as_view(), name='devicetype_add_rearport'),
|
||||
path(r'device-types/<int:pk>/rear-ports/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_rearport'),
|
||||
|
||||
# Device bay templates
|
||||
url(r'^device-types/(?P<pk>\d+)/device-bays/add/$', views.DeviceBayTemplateCreateView.as_view(), name='devicetype_add_devicebay'),
|
||||
url(r'^device-types/(?P<pk>\d+)/device-bays/delete/$', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicetype_delete_devicebay'),
|
||||
path(r'device-types/<int:pk>/device-bays/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicetype_add_devicebay'),
|
||||
path(r'device-types/<int:pk>/device-bays/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicetype_delete_devicebay'),
|
||||
|
||||
# Device roles
|
||||
url(r'^device-roles/$', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
||||
url(r'^device-roles/add/$', views.DeviceRoleCreateView.as_view(), name='devicerole_add'),
|
||||
url(r'^device-roles/import/$', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
|
||||
url(r'^device-roles/delete/$', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
|
||||
url(r'^device-roles/(?P<slug>[\w-]+)/edit/$', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
|
||||
url(r'^device-roles/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
|
||||
path(r'device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
||||
path(r'device-roles/add/', views.DeviceRoleCreateView.as_view(), name='devicerole_add'),
|
||||
path(r'device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
|
||||
path(r'device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
|
||||
path(r'device-roles/<slug:slug>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
|
||||
path(r'device-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
|
||||
|
||||
# Platforms
|
||||
url(r'^platforms/$', views.PlatformListView.as_view(), name='platform_list'),
|
||||
url(r'^platforms/add/$', views.PlatformCreateView.as_view(), name='platform_add'),
|
||||
url(r'^platforms/import/$', views.PlatformBulkImportView.as_view(), name='platform_import'),
|
||||
url(r'^platforms/delete/$', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
|
||||
url(r'^platforms/(?P<slug>[\w-]+)/edit/$', views.PlatformEditView.as_view(), name='platform_edit'),
|
||||
url(r'^platforms/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
|
||||
path(r'platforms/', views.PlatformListView.as_view(), name='platform_list'),
|
||||
path(r'platforms/add/', views.PlatformCreateView.as_view(), name='platform_add'),
|
||||
path(r'platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
|
||||
path(r'platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
|
||||
path(r'platforms/<slug:slug>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
|
||||
path(r'platforms/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
|
||||
|
||||
# Devices
|
||||
url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'),
|
||||
url(r'^devices/add/$', views.DeviceCreateView.as_view(), name='device_add'),
|
||||
url(r'^devices/import/$', views.DeviceBulkImportView.as_view(), name='device_import'),
|
||||
url(r'^devices/import/child-devices/$', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
|
||||
url(r'^devices/edit/$', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
|
||||
url(r'^devices/delete/$', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
|
||||
url(r'^devices/(?P<pk>\d+)/$', views.DeviceView.as_view(), name='device'),
|
||||
url(r'^devices/(?P<pk>\d+)/edit/$', views.DeviceEditView.as_view(), name='device_edit'),
|
||||
url(r'^devices/(?P<pk>\d+)/delete/$', views.DeviceDeleteView.as_view(), name='device_delete'),
|
||||
url(r'^devices/(?P<pk>\d+)/config-context/$', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
|
||||
url(r'^devices/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
|
||||
url(r'^devices/(?P<pk>\d+)/inventory/$', views.DeviceInventoryView.as_view(), name='device_inventory'),
|
||||
url(r'^devices/(?P<pk>\d+)/status/$', views.DeviceStatusView.as_view(), name='device_status'),
|
||||
url(r'^devices/(?P<pk>\d+)/lldp-neighbors/$', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'),
|
||||
url(r'^devices/(?P<pk>\d+)/config/$', views.DeviceConfigView.as_view(), name='device_config'),
|
||||
url(r'^devices/(?P<pk>\d+)/add-secret/$', secret_add, name='device_addsecret'),
|
||||
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceCreateView.as_view(), name='device_service_assign'),
|
||||
url(r'^devices/(?P<object_id>\d+)/images/add/$', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}),
|
||||
path(r'devices/', views.DeviceListView.as_view(), name='device_list'),
|
||||
path(r'devices/add/', views.DeviceCreateView.as_view(), name='device_add'),
|
||||
path(r'devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
|
||||
path(r'devices/import/child-devices/', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
|
||||
path(r'devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
|
||||
path(r'devices/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
|
||||
path(r'devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
|
||||
path(r'devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
|
||||
path(r'devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
|
||||
path(r'devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
|
||||
path(r'devices/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
|
||||
path(r'devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
|
||||
path(r'devices/<int:pk>/status/', views.DeviceStatusView.as_view(), name='device_status'),
|
||||
path(r'devices/<int:pk>/lldp-neighbors/', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'),
|
||||
path(r'devices/<int:pk>/config/', views.DeviceConfigView.as_view(), name='device_config'),
|
||||
path(r'devices/<int:pk>/add-secret/', secret_add, name='device_addsecret'),
|
||||
path(r'devices/<int:device>/services/assign/', ServiceCreateView.as_view(), name='device_service_assign'),
|
||||
path(r'devices/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}),
|
||||
|
||||
# Console ports
|
||||
url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
||||
url(r'^devices/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||
url(r'^console-ports/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
|
||||
url(r'^console-ports/(?P<pk>\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
||||
url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
||||
url(r'^console-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
|
||||
path(r'devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
||||
path(r'devices/<int:pk>/console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
||||
path(r'devices/<int:pk>/console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||
path(r'console-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
|
||||
path(r'console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
||||
path(r'console-ports/<int:pk>/delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
||||
path(r'console-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
|
||||
|
||||
# Console server ports
|
||||
url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
|
||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/edit/$', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
|
||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
||||
url(r'^console-server-ports/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
|
||||
url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
||||
url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
||||
url(r'^console-server-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
|
||||
url(r'^console-server-ports/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
|
||||
url(r'^console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
||||
path(r'devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
|
||||
path(r'devices/<int:pk>/console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
||||
path(r'devices/<int:pk>/console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
|
||||
path(r'devices/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
||||
path(r'console-server-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
|
||||
path(r'console-server-ports/<int:pk>/edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
||||
path(r'console-server-ports/<int:pk>/delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
||||
path(r'console-server-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
|
||||
path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
|
||||
path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
||||
|
||||
# Power ports
|
||||
url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
||||
url(r'^devices/(?P<pk>\d+)/power-ports/add/$', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||
url(r'^power-ports/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
|
||||
url(r'^power-ports/(?P<pk>\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
||||
url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
||||
url(r'^power-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
|
||||
path(r'devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
||||
path(r'devices/<int:pk>/power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
||||
path(r'devices/<int:pk>/power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||
path(r'power-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
|
||||
path(r'power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
||||
path(r'power-ports/<int:pk>/delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
||||
path(r'power-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
|
||||
|
||||
# Power outlets
|
||||
url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
|
||||
url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/power-outlets/edit/$', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
|
||||
url(r'^devices/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
||||
url(r'^power-outlets/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
|
||||
url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
||||
url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
||||
url(r'^power-outlets/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
|
||||
url(r'^power-outlets/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
|
||||
url(r'^power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
||||
path(r'devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
|
||||
path(r'devices/<int:pk>/power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
||||
path(r'devices/<int:pk>/power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
|
||||
path(r'devices/<int:pk>/power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
||||
path(r'power-outlets/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
|
||||
path(r'power-outlets/<int:pk>/edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
||||
path(r'power-outlets/<int:pk>/delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
||||
path(r'power-outlets/<int:pk>/trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
|
||||
path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
|
||||
path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
||||
|
||||
# Interfaces
|
||||
url(r'^devices/interfaces/add/$', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
|
||||
url(r'^devices/(?P<pk>\d+)/interfaces/add/$', views.InterfaceCreateView.as_view(), name='interface_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||
url(r'^interfaces/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
|
||||
url(r'^interfaces/(?P<pk>\d+)/$', views.InterfaceView.as_view(), name='interface'),
|
||||
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||
url(r'^interfaces/(?P<pk>\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
|
||||
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||
url(r'^interfaces/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
|
||||
url(r'^interfaces/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
|
||||
url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
|
||||
url(r'^interfaces/disconnect/$', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
|
||||
path(r'devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
|
||||
path(r'devices/<int:pk>/interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
|
||||
path(r'devices/<int:pk>/interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||
path(r'devices/<int:pk>/interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||
path(r'interfaces/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
|
||||
path(r'interfaces/<int:pk>/', views.InterfaceView.as_view(), name='interface'),
|
||||
path(r'interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||
path(r'interfaces/<int:pk>/assign-vlans/', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
|
||||
path(r'interfaces/<int:pk>/delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||
path(r'interfaces/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
|
||||
path(r'interfaces/<int:pk>/trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
|
||||
path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
|
||||
path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
|
||||
|
||||
# Front ports
|
||||
# url(r'^devices/front-ports/add/$', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
|
||||
url(r'^devices/(?P<pk>\d+)/front-ports/add/$', views.FrontPortCreateView.as_view(), name='frontport_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/front-ports/edit/$', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
|
||||
url(r'^devices/(?P<pk>\d+)/front-ports/delete/$', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
|
||||
url(r'^front-ports/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
|
||||
url(r'^front-ports/(?P<pk>\d+)/edit/$', views.FrontPortEditView.as_view(), name='frontport_edit'),
|
||||
url(r'^front-ports/(?P<pk>\d+)/delete/$', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
|
||||
url(r'^front-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
|
||||
url(r'^front-ports/rename/$', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
|
||||
url(r'^front-ports/disconnect/$', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
|
||||
# path(r'devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
|
||||
path(r'devices/<int:pk>/front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'),
|
||||
path(r'devices/<int:pk>/front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
|
||||
path(r'devices/<int:pk>/front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
|
||||
path(r'front-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
|
||||
path(r'front-ports/<int:pk>/edit/', views.FrontPortEditView.as_view(), name='frontport_edit'),
|
||||
path(r'front-ports/<int:pk>/delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
|
||||
path(r'front-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
|
||||
path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
|
||||
path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
|
||||
|
||||
# Rear ports
|
||||
# url(r'^devices/rear-ports/add/$', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
||||
url(r'^devices/(?P<pk>\d+)/rear-ports/add/$', views.RearPortCreateView.as_view(), name='rearport_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/rear-ports/edit/$', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
|
||||
url(r'^devices/(?P<pk>\d+)/rear-ports/delete/$', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
|
||||
url(r'^rear-ports/(?P<termination_a_id>\d+)/connect/(?P<termination_b_type>[\w-]+)/$', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
||||
url(r'^rear-ports/(?P<pk>\d+)/edit/$', views.RearPortEditView.as_view(), name='rearport_edit'),
|
||||
url(r'^rear-ports/(?P<pk>\d+)/delete/$', views.RearPortDeleteView.as_view(), name='rearport_delete'),
|
||||
url(r'^rear-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
|
||||
url(r'^rear-ports/rename/$', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
|
||||
url(r'^rear-ports/disconnect/$', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
|
||||
# path(r'devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
||||
path(r'devices/<int:pk>/rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'),
|
||||
path(r'devices/<int:pk>/rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
|
||||
path(r'devices/<int:pk>/rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
|
||||
path(r'rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
||||
path(r'rear-ports/<int:pk>/edit/', views.RearPortEditView.as_view(), name='rearport_edit'),
|
||||
path(r'rear-ports/<int:pk>/delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'),
|
||||
path(r'rear-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
|
||||
path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
|
||||
path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
|
||||
|
||||
# Device bays
|
||||
url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
||||
url(r'^devices/(?P<pk>\d+)/bays/add/$', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
||||
url(r'^devices/(?P<pk>\d+)/bays/delete/$', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
||||
url(r'^device-bays/(?P<pk>\d+)/edit/$', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
||||
url(r'^device-bays/(?P<pk>\d+)/delete/$', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
|
||||
url(r'^device-bays/(?P<pk>\d+)/populate/$', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
|
||||
url(r'^device-bays/(?P<pk>\d+)/depopulate/$', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
|
||||
url(r'^device-bays/rename/$', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
|
||||
path(r'devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
||||
path(r'devices/<int:pk>/bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
||||
path(r'devices/<int:pk>/bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
||||
path(r'device-bays/<int:pk>/edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
||||
path(r'device-bays/<int:pk>/delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
|
||||
path(r'device-bays/<int:pk>/populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
|
||||
path(r'device-bays/<int:pk>/depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
|
||||
path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
|
||||
|
||||
# Inventory items
|
||||
url(r'^inventory-items/$', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
|
||||
url(r'^inventory-items/import/$', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
|
||||
url(r'^inventory-items/edit/$', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
|
||||
url(r'^inventory-items/delete/$', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
|
||||
url(r'^inventory-items/(?P<pk>\d+)/edit/$', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
|
||||
url(r'^inventory-items/(?P<pk>\d+)/delete/$', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
|
||||
url(r'^devices/(?P<device>\d+)/inventory-items/add/$', views.InventoryItemEditView.as_view(), name='inventoryitem_add'),
|
||||
path(r'inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
|
||||
path(r'inventory-items/import/', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
|
||||
path(r'inventory-items/edit/', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
|
||||
path(r'inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
|
||||
path(r'inventory-items/<int:pk>/edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
|
||||
path(r'inventory-items/<int:pk>/delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
|
||||
path(r'devices/<int:device>/inventory-items/add/', views.InventoryItemEditView.as_view(), name='inventoryitem_add'),
|
||||
|
||||
# Cables
|
||||
url(r'^cables/$', views.CableListView.as_view(), name='cable_list'),
|
||||
url(r'^cables/import/$', views.CableBulkImportView.as_view(), name='cable_import'),
|
||||
url(r'^cables/edit/$', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
|
||||
url(r'^cables/delete/$', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
|
||||
url(r'^cables/(?P<pk>\d+)/$', views.CableView.as_view(), name='cable'),
|
||||
url(r'^cables/(?P<pk>\d+)/edit/$', views.CableEditView.as_view(), name='cable_edit'),
|
||||
url(r'^cables/(?P<pk>\d+)/delete/$', views.CableDeleteView.as_view(), name='cable_delete'),
|
||||
url(r'^cables/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='cable_changelog', kwargs={'model': Cable}),
|
||||
path(r'cables/', views.CableListView.as_view(), name='cable_list'),
|
||||
path(r'cables/import/', views.CableBulkImportView.as_view(), name='cable_import'),
|
||||
path(r'cables/edit/', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
|
||||
path(r'cables/delete/', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
|
||||
path(r'cables/<int:pk>/', views.CableView.as_view(), name='cable'),
|
||||
path(r'cables/<int:pk>/edit/', views.CableEditView.as_view(), name='cable_edit'),
|
||||
path(r'cables/<int:pk>/delete/', views.CableDeleteView.as_view(), name='cable_delete'),
|
||||
path(r'cables/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cable_changelog', kwargs={'model': Cable}),
|
||||
|
||||
# Console/power/interface connections (read-only)
|
||||
url(r'^console-connections/$', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
|
||||
url(r'^power-connections/$', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
|
||||
url(r'^interface-connections/$', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
|
||||
path(r'console-connections/', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
|
||||
path(r'power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
|
||||
path(r'interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
|
||||
|
||||
# Virtual chassis
|
||||
url(r'^virtual-chassis/$', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
|
||||
url(r'^virtual-chassis/add/$', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
|
||||
url(r'^virtual-chassis/(?P<pk>\d+)/edit/$', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
|
||||
url(r'^virtual-chassis/(?P<pk>\d+)/delete/$', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
|
||||
url(r'^virtual-chassis/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}),
|
||||
url(r'^virtual-chassis/(?P<pk>\d+)/add-member/$', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
|
||||
url(r'^virtual-chassis-members/(?P<pk>\d+)/delete/$', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
|
||||
path(r'virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
|
||||
path(r'virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
|
||||
path(r'virtual-chassis/<int:pk>/edit/', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
|
||||
path(r'virtual-chassis/<int:pk>/delete/', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
|
||||
path(r'virtual-chassis/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}),
|
||||
path(r'virtual-chassis/<int:pk>/add-member/', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
|
||||
path(r'virtual-chassis-members/<int:pk>/delete/', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
|
||||
|
||||
# Power panels
|
||||
url(r'^power-panels/$', views.PowerPanelListView.as_view(), name='powerpanel_list'),
|
||||
url(r'^power-panels/add/$', views.PowerPanelCreateView.as_view(), name='powerpanel_add'),
|
||||
url(r'^power-panels/import/$', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
|
||||
url(r'^power-panels/delete/$', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
|
||||
url(r'^power-panels/(?P<pk>\d+)/$', views.PowerPanelView.as_view(), name='powerpanel'),
|
||||
url(r'^power-panels/(?P<pk>\d+)/edit/$', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
|
||||
url(r'^power-panels/(?P<pk>\d+)/delete/$', views.PowerPanelDeleteView.as_view(), name='powerpanel_delete'),
|
||||
url(r'^power-panels/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='powerpanel_changelog', kwargs={'model': PowerPanel}),
|
||||
path(r'power-panels/', views.PowerPanelListView.as_view(), name='powerpanel_list'),
|
||||
path(r'power-panels/add/', views.PowerPanelCreateView.as_view(), name='powerpanel_add'),
|
||||
path(r'power-panels/import/', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
|
||||
path(r'power-panels/delete/', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
|
||||
path(r'power-panels/<int:pk>/', views.PowerPanelView.as_view(), name='powerpanel'),
|
||||
path(r'power-panels/<int:pk>/edit/', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
|
||||
path(r'power-panels/<int:pk>/delete/', views.PowerPanelDeleteView.as_view(), name='powerpanel_delete'),
|
||||
path(r'power-panels/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerpanel_changelog', kwargs={'model': PowerPanel}),
|
||||
|
||||
# Power feeds
|
||||
url(r'^power-feeds/$', views.PowerFeedListView.as_view(), name='powerfeed_list'),
|
||||
url(r'^power-feeds/add/$', views.PowerFeedEditView.as_view(), name='powerfeed_add'),
|
||||
url(r'^power-feeds/import/$', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
|
||||
url(r'^power-feeds/edit/$', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
|
||||
url(r'^power-feeds/delete/$', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/$', views.PowerFeedView.as_view(), name='powerfeed'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/edit/$', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/delete/$', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='powerfeed_changelog', kwargs={'model': PowerFeed}),
|
||||
path(r'power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'),
|
||||
path(r'power-feeds/add/', views.PowerFeedEditView.as_view(), name='powerfeed_add'),
|
||||
path(r'power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
|
||||
path(r'power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
|
||||
path(r'power-feeds/delete/', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
|
||||
path(r'power-feeds/<int:pk>/', views.PowerFeedView.as_view(), name='powerfeed'),
|
||||
path(r'power-feeds/<int:pk>/edit/', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
|
||||
path(r'power-feeds/<int:pk>/delete/', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
|
||||
path(r'power-feeds/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerfeed_changelog', kwargs={'model': PowerFeed}),
|
||||
|
||||
]
|
||||
|
@ -253,6 +253,14 @@ class SiteBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
default_return_url = 'dcim:site_list'
|
||||
|
||||
|
||||
class SiteBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_site'
|
||||
queryset = Site.objects.select_related('region', 'tenant')
|
||||
filter = filters.SiteFilter
|
||||
table = tables.SiteTable
|
||||
default_return_url = 'dcim:site_list'
|
||||
|
||||
|
||||
#
|
||||
# Rack groups
|
||||
#
|
||||
@ -464,7 +472,7 @@ class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
|
||||
class RackReservationListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_rackreservation'
|
||||
queryset = RackReservation.objects.all()
|
||||
queryset = RackReservation.objects.select_related('rack__site')
|
||||
filter = filters.RackReservationFilter
|
||||
filter_form = forms.RackReservationFilterForm
|
||||
table = tables.RackReservationTable
|
||||
@ -523,6 +531,7 @@ class ManufacturerListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_manufacturer'
|
||||
queryset = Manufacturer.objects.annotate(
|
||||
devicetype_count=Count('device_types', distinct=True),
|
||||
inventoryitem_count=Count('inventory_items', distinct=True),
|
||||
platform_count=Count('platforms', distinct=True),
|
||||
)
|
||||
table = tables.ManufacturerTable
|
||||
|
@ -30,10 +30,6 @@ def cache_changed_object(instance, **kwargs):
|
||||
|
||||
def _record_object_deleted(request, instance, **kwargs):
|
||||
|
||||
# Force resolution of request.user in case it's still a SimpleLazyObject. This seems to happen
|
||||
# occasionally during tests, but haven't been able to determine why.
|
||||
assert request.user.is_authenticated
|
||||
|
||||
# Record that the object was deleted
|
||||
if hasattr(instance, 'log_change'):
|
||||
instance.log_change(request.user, request.id, OBJECTCHANGE_ACTION_DELETE)
|
||||
@ -53,7 +49,7 @@ class ObjectChangeMiddleware(object):
|
||||
2. Enqueue any relevant webhooks.
|
||||
3. Increment metric counter for the event type
|
||||
|
||||
The post_save and pre_delete signals are employed to catch object modifications, however changes are recorded a bit
|
||||
The post_save and post_delete signals are employed to catch object modifications, however changes are recorded a bit
|
||||
differently for each. Objects being saved are cached into thread-local storage for action *after* the response has
|
||||
completed. This ensures that serialization of the object is performed only after any related objects (e.g. tags)
|
||||
have been created. Conversely, deletions are acted upon immediately, so that the serialized representation of the
|
||||
@ -71,10 +67,10 @@ class ObjectChangeMiddleware(object):
|
||||
# the same request.
|
||||
request.id = uuid.uuid4()
|
||||
|
||||
# Signals don't include the request context, so we're currying it into the pre_delete function ahead of time.
|
||||
# Signals don't include the request context, so we're currying it into the post_delete function ahead of time.
|
||||
record_object_deleted = curry(_record_object_deleted, request)
|
||||
|
||||
# Connect our receivers to the post_save and pre_delete signals.
|
||||
# Connect our receivers to the post_save and post_delete signals.
|
||||
post_save.connect(cache_changed_object, dispatch_uid='record_object_saved')
|
||||
post_delete.connect(record_object_deleted, dispatch_uid='record_object_deleted')
|
||||
|
||||
|
@ -108,17 +108,22 @@ class Webhook(models.Model):
|
||||
#
|
||||
|
||||
class CustomFieldModel(models.Model):
|
||||
_cf = None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def cf(self):
|
||||
"""
|
||||
Name-based CustomFieldValue accessor for use in templates
|
||||
"""
|
||||
if not hasattr(self, 'get_custom_fields'):
|
||||
return dict()
|
||||
return {field.name: value for field, value in self.get_custom_fields().items()}
|
||||
if self._cf is None:
|
||||
# Cache all custom field values for this instance
|
||||
self._cf = {
|
||||
field.name: value for field, value in self.get_custom_fields().items()
|
||||
}
|
||||
return self._cf
|
||||
|
||||
def get_custom_fields(self):
|
||||
"""
|
||||
@ -131,7 +136,7 @@ class CustomFieldModel(models.Model):
|
||||
|
||||
# If the object exists, populate its custom fields with values
|
||||
if hasattr(self, 'pk'):
|
||||
values = CustomFieldValue.objects.filter(obj_type=content_type, obj_id=self.pk).select_related('field')
|
||||
values = self.custom_field_values.all()
|
||||
values_dict = {cfv.field_id: cfv.value for cfv in values}
|
||||
return OrderedDict([(field, values_dict.get(field.pk)) for field in fields])
|
||||
else:
|
||||
|
@ -1,6 +1,24 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.db.models import Q, QuerySet
|
||||
|
||||
|
||||
class CustomFieldQueryset:
|
||||
"""
|
||||
Annotate custom fields on objects within a QuerySet.
|
||||
"""
|
||||
def __init__(self, queryset, custom_fields):
|
||||
self.queryset = queryset
|
||||
self.model = queryset.model
|
||||
self.custom_fields = custom_fields
|
||||
|
||||
def __iter__(self):
|
||||
for obj in self.queryset:
|
||||
values_dict = {cfv.field_id: cfv.value for cfv in obj.custom_field_values.all()}
|
||||
obj.custom_fields = OrderedDict([(field, values_dict.get(field.pk)) for field in self.custom_fields])
|
||||
yield obj
|
||||
|
||||
|
||||
class ConfigContextQuerySet(QuerySet):
|
||||
|
||||
def get_for_object(self, obj):
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from extras import views
|
||||
from extras.models import Tag
|
||||
@ -8,33 +8,33 @@ app_name = 'extras'
|
||||
urlpatterns = [
|
||||
|
||||
# Tags
|
||||
url(r'^tags/$', views.TagListView.as_view(), name='tag_list'),
|
||||
url(r'^tags/delete/$', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
|
||||
url(r'^tags/(?P<slug>[\w-]+)/$', views.TagView.as_view(), name='tag'),
|
||||
url(r'^tags/(?P<slug>[\w-]+)/edit/$', views.TagEditView.as_view(), name='tag_edit'),
|
||||
url(r'^tags/(?P<slug>[\w-]+)/delete/$', views.TagDeleteView.as_view(), name='tag_delete'),
|
||||
url(r'^tags/(?P<slug>[\w-]+)/changelog/$', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),
|
||||
path(r'tags/', views.TagListView.as_view(), name='tag_list'),
|
||||
path(r'tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
|
||||
path(r'tags/<slug:slug>/', views.TagView.as_view(), name='tag'),
|
||||
path(r'tags/<slug:slug>/edit/', views.TagEditView.as_view(), name='tag_edit'),
|
||||
path(r'tags/<slug:slug>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
|
||||
path(r'tags/<slug:slug>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),
|
||||
|
||||
# Config contexts
|
||||
url(r'^config-contexts/$', views.ConfigContextListView.as_view(), name='configcontext_list'),
|
||||
url(r'^config-contexts/add/$', views.ConfigContextCreateView.as_view(), name='configcontext_add'),
|
||||
url(r'^config-contexts/edit/$', views.ConfigContextBulkEditView.as_view(), name='configcontext_bulk_edit'),
|
||||
url(r'^config-contexts/delete/$', views.ConfigContextBulkDeleteView.as_view(), name='configcontext_bulk_delete'),
|
||||
url(r'^config-contexts/(?P<pk>\d+)/$', views.ConfigContextView.as_view(), name='configcontext'),
|
||||
url(r'^config-contexts/(?P<pk>\d+)/edit/$', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
|
||||
url(r'^config-contexts/(?P<pk>\d+)/delete/$', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
|
||||
path(r'config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'),
|
||||
path(r'config-contexts/add/', views.ConfigContextCreateView.as_view(), name='configcontext_add'),
|
||||
path(r'config-contexts/edit/', views.ConfigContextBulkEditView.as_view(), name='configcontext_bulk_edit'),
|
||||
path(r'config-contexts/delete/', views.ConfigContextBulkDeleteView.as_view(), name='configcontext_bulk_delete'),
|
||||
path(r'config-contexts/<int:pk>/', views.ConfigContextView.as_view(), name='configcontext'),
|
||||
path(r'config-contexts/<int:pk>/edit/', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
|
||||
path(r'config-contexts/<int:pk>/delete/', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
|
||||
|
||||
# Image attachments
|
||||
url(r'^image-attachments/(?P<pk>\d+)/edit/$', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
|
||||
url(r'^image-attachments/(?P<pk>\d+)/delete/$', views.ImageAttachmentDeleteView.as_view(), name='imageattachment_delete'),
|
||||
path(r'image-attachments/<int:pk>/edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
|
||||
path(r'image-attachments/<int:pk>/delete/', views.ImageAttachmentDeleteView.as_view(), name='imageattachment_delete'),
|
||||
|
||||
# Reports
|
||||
url(r'^reports/$', views.ReportListView.as_view(), name='report_list'),
|
||||
url(r'^reports/(?P<name>[^/]+\.[^/]+)/$', views.ReportView.as_view(), name='report'),
|
||||
url(r'^reports/(?P<name>[^/]+\.[^/]+)/run/$', views.ReportRunView.as_view(), name='report_run'),
|
||||
path(r'reports/', views.ReportListView.as_view(), name='report_list'),
|
||||
path(r'reports/<str:name>/', views.ReportView.as_view(), name='report'),
|
||||
path(r'reports/<str:name>/run/', views.ReportRunView.as_view(), name='report_run'),
|
||||
|
||||
# Change logging
|
||||
url(r'^changelog/$', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
||||
url(r'^changelog/(?P<pk>\d+)/$', views.ObjectChangeView.as_view(), name='objectchange'),
|
||||
path(r'changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
||||
path(r'changelog/<int:pk>/', views.ObjectChangeView.as_view(), name='objectchange'),
|
||||
|
||||
]
|
||||
|
@ -6,14 +6,14 @@ from netaddr.core import AddrFormatError
|
||||
|
||||
from dcim.models import Site, Device, Interface
|
||||
from extras.filters import CustomFieldFilterSet
|
||||
from tenancy.models import Tenant
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
|
||||
from virtualization.models import VirtualMachine
|
||||
from .constants import IPADDRESS_ROLE_CHOICES, IPADDRESS_STATUS_CHOICES, PREFIX_STATUS_CHOICES, VLAN_STATUS_CHOICES
|
||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
||||
|
||||
|
||||
class VRFFilter(CustomFieldFilterSet):
|
||||
class VRFFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -22,16 +22,6 @@ class VRFFilter(CustomFieldFilterSet):
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
@ -120,7 +110,7 @@ class RoleFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class PrefixFilter(CustomFieldFilterSet):
|
||||
class PrefixFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -159,16 +149,6 @@ class PrefixFilter(CustomFieldFilterSet):
|
||||
to_field_name='rd',
|
||||
label='VRF (RD)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Site.objects.all(),
|
||||
label='Site (ID)',
|
||||
@ -267,7 +247,7 @@ class PrefixFilter(CustomFieldFilterSet):
|
||||
return queryset.filter(prefix__net_mask_length=value)
|
||||
|
||||
|
||||
class IPAddressFilter(CustomFieldFilterSet):
|
||||
class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -298,16 +278,6 @@ class IPAddressFilter(CustomFieldFilterSet):
|
||||
to_field_name='rd',
|
||||
label='VRF (RD)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
device = django_filters.CharFilter(
|
||||
method='filter_device',
|
||||
field_name='name',
|
||||
@ -329,6 +299,12 @@ class IPAddressFilter(CustomFieldFilterSet):
|
||||
to_field_name='name',
|
||||
label='Virtual machine (name)',
|
||||
)
|
||||
interface = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='interface__name',
|
||||
queryset=Interface.objects.all(),
|
||||
to_field_name='name',
|
||||
label='Interface (ID)',
|
||||
)
|
||||
interface_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Interface.objects.all(),
|
||||
label='Interface (ID)',
|
||||
@ -408,7 +384,7 @@ class VLANGroupFilter(NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class VLANFilter(CustomFieldFilterSet):
|
||||
class VLANFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -437,16 +413,6 @@ class VLANFilter(CustomFieldFilterSet):
|
||||
to_field_name='slug',
|
||||
label='Group',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Role.objects.all(),
|
||||
label='Role (ID)',
|
||||
|
@ -6,6 +6,7 @@ from taggit.forms import TagField
|
||||
from dcim.models import Site, Rack, Device, Interface
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditNullBooleanSelect, ChainedModelChoiceField,
|
||||
@ -97,22 +98,13 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm
|
||||
]
|
||||
|
||||
|
||||
class VRFFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = VRF
|
||||
field_order = ['q', 'tenant_group', 'tenant']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
@ -497,8 +489,12 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
||||
]
|
||||
|
||||
|
||||
class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = Prefix
|
||||
field_order = [
|
||||
'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'status', 'site', 'role', 'tenant_group', 'tenant',
|
||||
'is_pool', 'expand',
|
||||
]
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -533,16 +529,6 @@ class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
choices=PREFIX_STATUS_CHOICES,
|
||||
required=False,
|
||||
@ -949,8 +935,11 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = IPAddress
|
||||
field_order = [
|
||||
'q', 'parent', 'family', 'mask_length', 'vrf_id', 'status', 'role', 'tenant_group', 'tenant',
|
||||
]
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -985,16 +974,6 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
choices=IPADDRESS_STATUS_CHOICES,
|
||||
required=False,
|
||||
@ -1226,8 +1205,9 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
||||
]
|
||||
|
||||
|
||||
class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = VLAN
|
||||
field_order = ['q', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -1251,16 +1231,6 @@ class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
status = forms.MultipleChoiceField(
|
||||
choices=VLAN_STATUS_CHOICES,
|
||||
required=False,
|
||||
|
@ -319,6 +319,7 @@ class PrefixTable(BaseTable):
|
||||
|
||||
class PrefixDetailTable(PrefixTable):
|
||||
utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False)
|
||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||
|
||||
class Meta(PrefixTable.Meta):
|
||||
fields = ('pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description')
|
||||
@ -351,6 +352,7 @@ class IPAddressDetailTable(IPAddressTable):
|
||||
nat_inside = tables.LinkColumn(
|
||||
'ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, verbose_name='NAT (Inside)'
|
||||
)
|
||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||
|
||||
class Meta(IPAddressTable.Meta):
|
||||
fields = (
|
||||
@ -426,6 +428,7 @@ class VLANTable(BaseTable):
|
||||
|
||||
class VLANDetailTable(VLANTable):
|
||||
prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes')
|
||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||
|
||||
class Meta(VLANTable.Meta):
|
||||
fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
|
||||
|
@ -1,3 +1,5 @@
|
||||
import json
|
||||
|
||||
from django.urls import reverse
|
||||
from netaddr import IPNetwork
|
||||
from rest_framework import status
|
||||
@ -870,6 +872,8 @@ class VLANTest(APITestCase):
|
||||
self.vlan2 = VLAN.objects.create(vid=2, name='Test VLAN 2')
|
||||
self.vlan3 = VLAN.objects.create(vid=3, name='Test VLAN 3')
|
||||
|
||||
self.prefix1 = Prefix.objects.create(prefix=IPNetwork('192.168.1.0/24'))
|
||||
|
||||
def test_get_vlan(self):
|
||||
|
||||
url = reverse('ipam-api:vlan-detail', kwargs={'pk': self.vlan1.pk})
|
||||
@ -960,6 +964,20 @@ class VLANTest(APITestCase):
|
||||
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual(VLAN.objects.count(), 2)
|
||||
|
||||
def test_delete_vlan_with_prefix(self):
|
||||
self.prefix1.vlan = self.vlan1
|
||||
self.prefix1.save()
|
||||
|
||||
url = reverse('ipam-api:vlan-detail', kwargs={'pk': self.vlan1.pk})
|
||||
response = self.client.delete(url, **self.header)
|
||||
|
||||
# can't use assertHttpStatus here because we don't have response.data
|
||||
self.assertEqual(response.status_code, 409)
|
||||
|
||||
content = json.loads(response.content.decode('utf-8'))
|
||||
self.assertIn('detail', content)
|
||||
self.assertTrue(content['detail'].startswith('Unable to delete object.'))
|
||||
|
||||
|
||||
class ServiceTest(APITestCase):
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from extras.views import ObjectChangeLogView
|
||||
from . import views
|
||||
@ -8,97 +8,97 @@ app_name = 'ipam'
|
||||
urlpatterns = [
|
||||
|
||||
# VRFs
|
||||
url(r'^vrfs/$', views.VRFListView.as_view(), name='vrf_list'),
|
||||
url(r'^vrfs/add/$', views.VRFCreateView.as_view(), name='vrf_add'),
|
||||
url(r'^vrfs/import/$', views.VRFBulkImportView.as_view(), name='vrf_import'),
|
||||
url(r'^vrfs/edit/$', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'),
|
||||
url(r'^vrfs/delete/$', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'),
|
||||
url(r'^vrfs/(?P<pk>\d+)/$', views.VRFView.as_view(), name='vrf'),
|
||||
url(r'^vrfs/(?P<pk>\d+)/edit/$', views.VRFEditView.as_view(), name='vrf_edit'),
|
||||
url(r'^vrfs/(?P<pk>\d+)/delete/$', views.VRFDeleteView.as_view(), name='vrf_delete'),
|
||||
url(r'^vrfs/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='vrf_changelog', kwargs={'model': VRF}),
|
||||
path(r'vrfs/', views.VRFListView.as_view(), name='vrf_list'),
|
||||
path(r'vrfs/add/', views.VRFCreateView.as_view(), name='vrf_add'),
|
||||
path(r'vrfs/import/', views.VRFBulkImportView.as_view(), name='vrf_import'),
|
||||
path(r'vrfs/edit/', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'),
|
||||
path(r'vrfs/delete/', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'),
|
||||
path(r'vrfs/<int:pk>/', views.VRFView.as_view(), name='vrf'),
|
||||
path(r'vrfs/<int:pk>/edit/', views.VRFEditView.as_view(), name='vrf_edit'),
|
||||
path(r'vrfs/<int:pk>/delete/', views.VRFDeleteView.as_view(), name='vrf_delete'),
|
||||
path(r'vrfs/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vrf_changelog', kwargs={'model': VRF}),
|
||||
|
||||
# RIRs
|
||||
url(r'^rirs/$', views.RIRListView.as_view(), name='rir_list'),
|
||||
url(r'^rirs/add/$', views.RIRCreateView.as_view(), name='rir_add'),
|
||||
url(r'^rirs/import/$', views.RIRBulkImportView.as_view(), name='rir_import'),
|
||||
url(r'^rirs/delete/$', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
|
||||
url(r'^rirs/(?P<slug>[\w-]+)/edit/$', views.RIREditView.as_view(), name='rir_edit'),
|
||||
url(r'^vrfs/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='rir_changelog', kwargs={'model': RIR}),
|
||||
path(r'rirs/', views.RIRListView.as_view(), name='rir_list'),
|
||||
path(r'rirs/add/', views.RIRCreateView.as_view(), name='rir_add'),
|
||||
path(r'rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'),
|
||||
path(r'rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
|
||||
path(r'rirs/<slug:slug>/edit/', views.RIREditView.as_view(), name='rir_edit'),
|
||||
path(r'vrfs/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='rir_changelog', kwargs={'model': RIR}),
|
||||
|
||||
# Aggregates
|
||||
url(r'^aggregates/$', views.AggregateListView.as_view(), name='aggregate_list'),
|
||||
url(r'^aggregates/add/$', views.AggregateCreateView.as_view(), name='aggregate_add'),
|
||||
url(r'^aggregates/import/$', views.AggregateBulkImportView.as_view(), name='aggregate_import'),
|
||||
url(r'^aggregates/edit/$', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
|
||||
url(r'^aggregates/delete/$', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
|
||||
url(r'^aggregates/(?P<pk>\d+)/$', views.AggregateView.as_view(), name='aggregate'),
|
||||
url(r'^aggregates/(?P<pk>\d+)/edit/$', views.AggregateEditView.as_view(), name='aggregate_edit'),
|
||||
url(r'^aggregates/(?P<pk>\d+)/delete/$', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
|
||||
url(r'^aggregates/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='aggregate_changelog', kwargs={'model': Aggregate}),
|
||||
path(r'aggregates/', views.AggregateListView.as_view(), name='aggregate_list'),
|
||||
path(r'aggregates/add/', views.AggregateCreateView.as_view(), name='aggregate_add'),
|
||||
path(r'aggregates/import/', views.AggregateBulkImportView.as_view(), name='aggregate_import'),
|
||||
path(r'aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
|
||||
path(r'aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
|
||||
path(r'aggregates/<int:pk>/', views.AggregateView.as_view(), name='aggregate'),
|
||||
path(r'aggregates/<int:pk>/edit/', views.AggregateEditView.as_view(), name='aggregate_edit'),
|
||||
path(r'aggregates/<int:pk>/delete/', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
|
||||
path(r'aggregates/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='aggregate_changelog', kwargs={'model': Aggregate}),
|
||||
|
||||
# Roles
|
||||
url(r'^roles/$', views.RoleListView.as_view(), name='role_list'),
|
||||
url(r'^roles/add/$', views.RoleCreateView.as_view(), name='role_add'),
|
||||
url(r'^roles/import/$', views.RoleBulkImportView.as_view(), name='role_import'),
|
||||
url(r'^roles/delete/$', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
|
||||
url(r'^roles/(?P<slug>[\w-]+)/edit/$', views.RoleEditView.as_view(), name='role_edit'),
|
||||
url(r'^roles/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='role_changelog', kwargs={'model': Role}),
|
||||
path(r'roles/', views.RoleListView.as_view(), name='role_list'),
|
||||
path(r'roles/add/', views.RoleCreateView.as_view(), name='role_add'),
|
||||
path(r'roles/import/', views.RoleBulkImportView.as_view(), name='role_import'),
|
||||
path(r'roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
|
||||
path(r'roles/<slug:slug>/edit/', views.RoleEditView.as_view(), name='role_edit'),
|
||||
path(r'roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='role_changelog', kwargs={'model': Role}),
|
||||
|
||||
# Prefixes
|
||||
url(r'^prefixes/$', views.PrefixListView.as_view(), name='prefix_list'),
|
||||
url(r'^prefixes/add/$', views.PrefixCreateView.as_view(), name='prefix_add'),
|
||||
url(r'^prefixes/import/$', views.PrefixBulkImportView.as_view(), name='prefix_import'),
|
||||
url(r'^prefixes/edit/$', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'),
|
||||
url(r'^prefixes/delete/$', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'),
|
||||
url(r'^prefixes/(?P<pk>\d+)/$', views.PrefixView.as_view(), name='prefix'),
|
||||
url(r'^prefixes/(?P<pk>\d+)/edit/$', views.PrefixEditView.as_view(), name='prefix_edit'),
|
||||
url(r'^prefixes/(?P<pk>\d+)/delete/$', views.PrefixDeleteView.as_view(), name='prefix_delete'),
|
||||
url(r'^prefixes/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}),
|
||||
url(r'^prefixes/(?P<pk>\d+)/prefixes/$', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'),
|
||||
url(r'^prefixes/(?P<pk>\d+)/ip-addresses/$', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'),
|
||||
path(r'prefixes/', views.PrefixListView.as_view(), name='prefix_list'),
|
||||
path(r'prefixes/add/', views.PrefixCreateView.as_view(), name='prefix_add'),
|
||||
path(r'prefixes/import/', views.PrefixBulkImportView.as_view(), name='prefix_import'),
|
||||
path(r'prefixes/edit/', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'),
|
||||
path(r'prefixes/delete/', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'),
|
||||
path(r'prefixes/<int:pk>/', views.PrefixView.as_view(), name='prefix'),
|
||||
path(r'prefixes/<int:pk>/edit/', views.PrefixEditView.as_view(), name='prefix_edit'),
|
||||
path(r'prefixes/<int:pk>/delete/', views.PrefixDeleteView.as_view(), name='prefix_delete'),
|
||||
path(r'prefixes/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}),
|
||||
path(r'prefixes/<int:pk>/prefixes/', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'),
|
||||
path(r'prefixes/<int:pk>/ip-addresses/', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'),
|
||||
|
||||
# IP addresses
|
||||
url(r'^ip-addresses/$', views.IPAddressListView.as_view(), name='ipaddress_list'),
|
||||
url(r'^ip-addresses/add/$', views.IPAddressCreateView.as_view(), name='ipaddress_add'),
|
||||
url(r'^ip-addresses/bulk-add/$', views.IPAddressBulkCreateView.as_view(), name='ipaddress_bulk_add'),
|
||||
url(r'^ip-addresses/import/$', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
|
||||
url(r'^ip-addresses/edit/$', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
|
||||
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
||||
url(r'^ip-addresses/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='ipaddress_changelog', kwargs={'model': IPAddress}),
|
||||
url(r'^ip-addresses/assign/$', views.IPAddressAssignView.as_view(), name='ipaddress_assign'),
|
||||
url(r'^ip-addresses/(?P<pk>\d+)/$', views.IPAddressView.as_view(), name='ipaddress'),
|
||||
url(r'^ip-addresses/(?P<pk>\d+)/edit/$', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
|
||||
url(r'^ip-addresses/(?P<pk>\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
|
||||
path(r'ip-addresses/', views.IPAddressListView.as_view(), name='ipaddress_list'),
|
||||
path(r'ip-addresses/add/', views.IPAddressCreateView.as_view(), name='ipaddress_add'),
|
||||
path(r'ip-addresses/bulk-add/', views.IPAddressBulkCreateView.as_view(), name='ipaddress_bulk_add'),
|
||||
path(r'ip-addresses/import/', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
|
||||
path(r'ip-addresses/edit/', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
|
||||
path(r'ip-addresses/delete/', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
||||
path(r'ip-addresses/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='ipaddress_changelog', kwargs={'model': IPAddress}),
|
||||
path(r'ip-addresses/assign/', views.IPAddressAssignView.as_view(), name='ipaddress_assign'),
|
||||
path(r'ip-addresses/<int:pk>/', views.IPAddressView.as_view(), name='ipaddress'),
|
||||
path(r'ip-addresses/<int:pk>/edit/', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
|
||||
path(r'ip-addresses/<int:pk>/delete/', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
|
||||
|
||||
# VLAN groups
|
||||
url(r'^vlan-groups/$', views.VLANGroupListView.as_view(), name='vlangroup_list'),
|
||||
url(r'^vlan-groups/add/$', views.VLANGroupCreateView.as_view(), name='vlangroup_add'),
|
||||
url(r'^vlan-groups/import/$', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
||||
url(r'^vlan-groups/delete/$', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
||||
url(r'^vlan-groups/(?P<pk>\d+)/edit/$', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
||||
url(r'^vlan-groups/(?P<pk>\d+)/vlans/$', views.VLANGroupVLANsView.as_view(), name='vlangroup_vlans'),
|
||||
url(r'^vlan-groups/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='vlangroup_changelog', kwargs={'model': VLANGroup}),
|
||||
path(r'vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'),
|
||||
path(r'vlan-groups/add/', views.VLANGroupCreateView.as_view(), name='vlangroup_add'),
|
||||
path(r'vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
||||
path(r'vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
||||
path(r'vlan-groups/<int:pk>/edit/', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
||||
path(r'vlan-groups/<int:pk>/vlans/', views.VLANGroupVLANsView.as_view(), name='vlangroup_vlans'),
|
||||
path(r'vlan-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlangroup_changelog', kwargs={'model': VLANGroup}),
|
||||
|
||||
# VLANs
|
||||
url(r'^vlans/$', views.VLANListView.as_view(), name='vlan_list'),
|
||||
url(r'^vlans/add/$', views.VLANCreateView.as_view(), name='vlan_add'),
|
||||
url(r'^vlans/import/$', views.VLANBulkImportView.as_view(), name='vlan_import'),
|
||||
url(r'^vlans/edit/$', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
|
||||
url(r'^vlans/delete/$', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
|
||||
url(r'^vlans/(?P<pk>\d+)/$', views.VLANView.as_view(), name='vlan'),
|
||||
url(r'^vlans/(?P<pk>\d+)/members/$', views.VLANMembersView.as_view(), name='vlan_members'),
|
||||
url(r'^vlans/(?P<pk>\d+)/edit/$', views.VLANEditView.as_view(), name='vlan_edit'),
|
||||
url(r'^vlans/(?P<pk>\d+)/delete/$', views.VLANDeleteView.as_view(), name='vlan_delete'),
|
||||
url(r'^vlans/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='vlan_changelog', kwargs={'model': VLAN}),
|
||||
path(r'vlans/', views.VLANListView.as_view(), name='vlan_list'),
|
||||
path(r'vlans/add/', views.VLANCreateView.as_view(), name='vlan_add'),
|
||||
path(r'vlans/import/', views.VLANBulkImportView.as_view(), name='vlan_import'),
|
||||
path(r'vlans/edit/', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
|
||||
path(r'vlans/delete/', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
|
||||
path(r'vlans/<int:pk>/', views.VLANView.as_view(), name='vlan'),
|
||||
path(r'vlans/<int:pk>/members/', views.VLANMembersView.as_view(), name='vlan_members'),
|
||||
path(r'vlans/<int:pk>/edit/', views.VLANEditView.as_view(), name='vlan_edit'),
|
||||
path(r'vlans/<int:pk>/delete/', views.VLANDeleteView.as_view(), name='vlan_delete'),
|
||||
path(r'vlans/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlan_changelog', kwargs={'model': VLAN}),
|
||||
|
||||
# Services
|
||||
url(r'^services/$', views.ServiceListView.as_view(), name='service_list'),
|
||||
url(r'^services/edit/$', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'),
|
||||
url(r'^services/delete/$', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'),
|
||||
url(r'^services/(?P<pk>\d+)/$', views.ServiceView.as_view(), name='service'),
|
||||
url(r'^services/(?P<pk>\d+)/edit/$', views.ServiceEditView.as_view(), name='service_edit'),
|
||||
url(r'^services/(?P<pk>\d+)/delete/$', views.ServiceDeleteView.as_view(), name='service_delete'),
|
||||
url(r'^services/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='service_changelog', kwargs={'model': Service}),
|
||||
path(r'services/', views.ServiceListView.as_view(), name='service_list'),
|
||||
path(r'services/edit/', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'),
|
||||
path(r'services/delete/', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'),
|
||||
path(r'services/<int:pk>/', views.ServiceView.as_view(), name='service'),
|
||||
path(r'services/<int:pk>/edit/', views.ServiceEditView.as_view(), name='service_edit'),
|
||||
path(r'services/<int:pk>/delete/', views.ServiceDeleteView.as_view(), name='service_delete'),
|
||||
path(r'services/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='service_changelog', kwargs={'model': Service}),
|
||||
|
||||
]
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.conf.urls import include
|
||||
from django.urls import path, re_path
|
||||
from django.views.static import serve
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
@ -24,63 +25,63 @@ schema_view = get_schema_view(
|
||||
_patterns = [
|
||||
|
||||
# Base views
|
||||
url(r'^$', HomeView.as_view(), name='home'),
|
||||
url(r'^search/$', SearchView.as_view(), name='search'),
|
||||
path(r'', HomeView.as_view(), name='home'),
|
||||
path(r'search/', SearchView.as_view(), name='search'),
|
||||
|
||||
# Login/logout
|
||||
url(r'^login/$', LoginView.as_view(), name='login'),
|
||||
url(r'^logout/$', LogoutView.as_view(), name='logout'),
|
||||
path(r'login/', LoginView.as_view(), name='login'),
|
||||
path(r'logout/', LogoutView.as_view(), name='logout'),
|
||||
|
||||
# Apps
|
||||
url(r'^circuits/', include('circuits.urls')),
|
||||
url(r'^dcim/', include('dcim.urls')),
|
||||
url(r'^extras/', include('extras.urls')),
|
||||
url(r'^ipam/', include('ipam.urls')),
|
||||
url(r'^secrets/', include('secrets.urls')),
|
||||
url(r'^tenancy/', include('tenancy.urls')),
|
||||
url(r'^user/', include('users.urls')),
|
||||
url(r'^virtualization/', include('virtualization.urls')),
|
||||
path(r'circuits/', include('circuits.urls')),
|
||||
path(r'dcim/', include('dcim.urls')),
|
||||
path(r'extras/', include('extras.urls')),
|
||||
path(r'ipam/', include('ipam.urls')),
|
||||
path(r'secrets/', include('secrets.urls')),
|
||||
path(r'tenancy/', include('tenancy.urls')),
|
||||
path(r'user/', include('users.urls')),
|
||||
path(r'virtualization/', include('virtualization.urls')),
|
||||
|
||||
# API
|
||||
url(r'^api/$', APIRootView.as_view(), name='api-root'),
|
||||
url(r'^api/circuits/', include('circuits.api.urls')),
|
||||
url(r'^api/dcim/', include('dcim.api.urls')),
|
||||
url(r'^api/extras/', include('extras.api.urls')),
|
||||
url(r'^api/ipam/', include('ipam.api.urls')),
|
||||
url(r'^api/secrets/', include('secrets.api.urls')),
|
||||
url(r'^api/tenancy/', include('tenancy.api.urls')),
|
||||
url(r'^api/virtualization/', include('virtualization.api.urls')),
|
||||
url(r'^api/docs/$', schema_view.with_ui('swagger'), name='api_docs'),
|
||||
url(r'^api/redoc/$', schema_view.with_ui('redoc'), name='api_redocs'),
|
||||
url(r'^api/swagger(?P<format>.json|.yaml)$', schema_view.without_ui(), name='schema_swagger'),
|
||||
path(r'api/', APIRootView.as_view(), name='api-root'),
|
||||
path(r'api/circuits/', include('circuits.api.urls')),
|
||||
path(r'api/dcim/', include('dcim.api.urls')),
|
||||
path(r'api/extras/', include('extras.api.urls')),
|
||||
path(r'api/ipam/', include('ipam.api.urls')),
|
||||
path(r'api/secrets/', include('secrets.api.urls')),
|
||||
path(r'api/tenancy/', include('tenancy.api.urls')),
|
||||
path(r'api/virtualization/', include('virtualization.api.urls')),
|
||||
path(r'api/docs/', schema_view.with_ui('swagger'), name='api_docs'),
|
||||
path(r'api/redoc/', schema_view.with_ui('redoc'), name='api_redocs'),
|
||||
re_path(r'^api/swagger(?P<format>.json|.yaml)$', schema_view.without_ui(), name='schema_swagger'),
|
||||
|
||||
# Serving static media in Django to pipe it through LoginRequiredMiddleware
|
||||
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
|
||||
path(r'media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
|
||||
|
||||
# Admin
|
||||
url(r'^admin/', admin_site.urls),
|
||||
path(r'admin/', admin_site.urls),
|
||||
|
||||
]
|
||||
|
||||
if settings.WEBHOOKS_ENABLED:
|
||||
_patterns += [
|
||||
url(r'^admin/webhook-backend-status/', include('django_rq.urls')),
|
||||
path(r'admin/webhook-backend-status/', include('django_rq.urls')),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
import debug_toolbar
|
||||
_patterns += [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
path(r'__debug__/', include(debug_toolbar.urls)),
|
||||
]
|
||||
|
||||
if settings.METRICS_ENABLED:
|
||||
_patterns += [
|
||||
url('', include('django_prometheus.urls')),
|
||||
path('', include('django_prometheus.urls')),
|
||||
]
|
||||
|
||||
# Prepend BASE_PATH
|
||||
urlpatterns = [
|
||||
url(r'^{}'.format(settings.BASE_PATH), include(_patterns))
|
||||
path(r'{}'.format(settings.BASE_PATH), include(_patterns))
|
||||
]
|
||||
|
||||
handler500 = 'utilities.views.server_error'
|
||||
|
@ -559,6 +559,7 @@ table.report th a {
|
||||
.color-block {
|
||||
display: block;
|
||||
width: 80px;
|
||||
border: 1px solid grey;
|
||||
}
|
||||
.text-nowrap {
|
||||
white-space: nowrap;
|
||||
|
@ -267,6 +267,10 @@ $(document).ready(function() {
|
||||
|
||||
processResults: function (data) {
|
||||
var results = $.map(data.results, function (obj) {
|
||||
// If tag contains space add double quotes
|
||||
if (/\s/.test(obj.name))
|
||||
obj.name = '"' + obj.name + '"'
|
||||
|
||||
return {
|
||||
id: obj.name,
|
||||
text: obj.name
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from extras.views import ObjectChangeLogView
|
||||
from . import views
|
||||
@ -8,21 +8,21 @@ app_name = 'secrets'
|
||||
urlpatterns = [
|
||||
|
||||
# Secret roles
|
||||
url(r'^secret-roles/$', views.SecretRoleListView.as_view(), name='secretrole_list'),
|
||||
url(r'^secret-roles/add/$', views.SecretRoleCreateView.as_view(), name='secretrole_add'),
|
||||
url(r'^secret-roles/import/$', views.SecretRoleBulkImportView.as_view(), name='secretrole_import'),
|
||||
url(r'^secret-roles/delete/$', views.SecretRoleBulkDeleteView.as_view(), name='secretrole_bulk_delete'),
|
||||
url(r'^secret-roles/(?P<slug>[\w-]+)/edit/$', views.SecretRoleEditView.as_view(), name='secretrole_edit'),
|
||||
url(r'^secret-roles/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='secretrole_changelog', kwargs={'model': SecretRole}),
|
||||
path(r'secret-roles/', views.SecretRoleListView.as_view(), name='secretrole_list'),
|
||||
path(r'secret-roles/add/', views.SecretRoleCreateView.as_view(), name='secretrole_add'),
|
||||
path(r'secret-roles/import/', views.SecretRoleBulkImportView.as_view(), name='secretrole_import'),
|
||||
path(r'secret-roles/delete/', views.SecretRoleBulkDeleteView.as_view(), name='secretrole_bulk_delete'),
|
||||
path(r'secret-roles/<slug:slug>/edit/', views.SecretRoleEditView.as_view(), name='secretrole_edit'),
|
||||
path(r'secret-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='secretrole_changelog', kwargs={'model': SecretRole}),
|
||||
|
||||
# Secrets
|
||||
url(r'^secrets/$', views.SecretListView.as_view(), name='secret_list'),
|
||||
url(r'^secrets/import/$', views.SecretBulkImportView.as_view(), name='secret_import'),
|
||||
url(r'^secrets/edit/$', views.SecretBulkEditView.as_view(), name='secret_bulk_edit'),
|
||||
url(r'^secrets/delete/$', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
||||
url(r'^secrets/(?P<pk>\d+)/$', views.SecretView.as_view(), name='secret'),
|
||||
url(r'^secrets/(?P<pk>\d+)/edit/$', views.secret_edit, name='secret_edit'),
|
||||
url(r'^secrets/(?P<pk>\d+)/delete/$', views.SecretDeleteView.as_view(), name='secret_delete'),
|
||||
url(r'^secrets/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='secret_changelog', kwargs={'model': Secret}),
|
||||
path(r'secrets/', views.SecretListView.as_view(), name='secret_list'),
|
||||
path(r'secrets/import/', views.SecretBulkImportView.as_view(), name='secret_import'),
|
||||
path(r'secrets/edit/', views.SecretBulkEditView.as_view(), name='secret_bulk_edit'),
|
||||
path(r'secrets/delete/', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
||||
path(r'secrets/<int:pk>/', views.SecretView.as_view(), name='secret'),
|
||||
path(r'secrets/<int:pk>/edit/', views.secret_edit, name='secret_edit'),
|
||||
path(r'secrets/<int:pk>/delete/', views.SecretDeleteView.as_view(), name='secret_delete'),
|
||||
path(r'secrets/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='secret_changelog', kwargs={'model': Secret}),
|
||||
|
||||
]
|
||||
|
@ -31,7 +31,7 @@
|
||||
</h4>
|
||||
<p><span class="label label-{% if cable.status %}success{% else %}info{% endif %}">{{ cable.get_status_display }}</span></p>
|
||||
<p>{{ cable.get_type_display|default:"" }}</p>
|
||||
{% if cable.length %}- {{ cable.length }}{{ cable.get_length_unit_display }}{% endif %}
|
||||
{% if cable.length %}{{ cable.length }} {{ cable.get_length_unit_display }}{% endif %}
|
||||
<span class="label color-block center-block" style="background-color: #{{ cable.color }}"> </span>
|
||||
{% else %}
|
||||
<h4 class="text-muted">No Cable</h4>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<h1>{% block title %}Sites{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:site_bulk_edit' %}
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:site_bulk_edit' bulk_delete_url='dcim:site_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
|
@ -435,7 +435,12 @@
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li><a href="{% url 'login' %}?next={{ request.path }}"><i class="fa fa-sign-in"></i> Log in</a></li>
|
||||
{% url 'login' as login_url %}
|
||||
{% if request.path == login_url %}
|
||||
<li><a href="{{ request.get_full_path }}"><i class="fa fa-sign-in"></i> Log in</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ login_url }}?next={{ request.get_full_path | urlencode }}"><i class="fa fa-sign-in"></i> Log in</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
<form action="{% url 'search' %}" method="get" class="navbar-form navbar-right" id="navbar_search" role="search">
|
||||
|
@ -25,6 +25,7 @@
|
||||
<div class="panel-body">
|
||||
{% csrf_token %}
|
||||
{% if 'next' in request.GET %}<input type="hidden" name="next" value="{{ request.GET.next }}" />{% endif %}
|
||||
{% if 'next' in request.POST %}<input type="hidden" name="next" value="{{ request.POST.next }}" />{% endif %}
|
||||
{% render_form form %}
|
||||
</div>
|
||||
<div class="panel-footer text-right">
|
||||
|
27
netbox/tenancy/filtersets.py
Normal file
27
netbox/tenancy/filtersets.py
Normal file
@ -0,0 +1,27 @@
|
||||
import django_filters
|
||||
from .models import Tenant, TenantGroup
|
||||
|
||||
|
||||
class TenancyFilterSet(django_filters.FilterSet):
|
||||
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__group__id',
|
||||
queryset=TenantGroup.objects.all(),
|
||||
to_field_name='id',
|
||||
label='Tenant Group (ID)',
|
||||
)
|
||||
tenant_group = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__group__slug',
|
||||
queryset=TenantGroup.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant Group (slug)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
@ -1,5 +1,4 @@
|
||||
from django import forms
|
||||
from django.db.models import Count
|
||||
from taggit.forms import TagField
|
||||
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||
@ -117,7 +116,7 @@ class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
|
||||
|
||||
#
|
||||
# Tenancy form extension
|
||||
# Form extensions
|
||||
#
|
||||
|
||||
class TenancyForm(ChainedFieldsMixin, forms.Form):
|
||||
@ -155,3 +154,29 @@ class TenancyForm(ChainedFieldsMixin, forms.Form):
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class TenancyFilterForm(forms.Form):
|
||||
tenant_group = FilterChoiceField(
|
||||
queryset=TenantGroup.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenant-groups/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
filter_for={
|
||||
'tenant': 'group'
|
||||
}
|
||||
)
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/tenancy/tenants/",
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from extras.views import ObjectChangeLogView
|
||||
from . import views
|
||||
@ -8,22 +8,22 @@ app_name = 'tenancy'
|
||||
urlpatterns = [
|
||||
|
||||
# Tenant groups
|
||||
url(r'^tenant-groups/$', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
|
||||
url(r'^tenant-groups/add/$', views.TenantGroupCreateView.as_view(), name='tenantgroup_add'),
|
||||
url(r'^tenant-groups/import/$', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
|
||||
url(r'^tenant-groups/delete/$', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
|
||||
url(r'^tenant-groups/(?P<slug>[\w-]+)/edit/$', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
|
||||
url(r'^tenant-groups/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='tenantgroup_changelog', kwargs={'model': TenantGroup}),
|
||||
path(r'tenant-groups/', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
|
||||
path(r'tenant-groups/add/', views.TenantGroupCreateView.as_view(), name='tenantgroup_add'),
|
||||
path(r'tenant-groups/import/', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
|
||||
path(r'tenant-groups/delete/', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
|
||||
path(r'tenant-groups/<slug:slug>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
|
||||
path(r'tenant-groups/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='tenantgroup_changelog', kwargs={'model': TenantGroup}),
|
||||
|
||||
# Tenants
|
||||
url(r'^tenants/$', views.TenantListView.as_view(), name='tenant_list'),
|
||||
url(r'^tenants/add/$', views.TenantCreateView.as_view(), name='tenant_add'),
|
||||
url(r'^tenants/import/$', views.TenantBulkImportView.as_view(), name='tenant_import'),
|
||||
url(r'^tenants/edit/$', views.TenantBulkEditView.as_view(), name='tenant_bulk_edit'),
|
||||
url(r'^tenants/delete/$', views.TenantBulkDeleteView.as_view(), name='tenant_bulk_delete'),
|
||||
url(r'^tenants/(?P<slug>[\w-]+)/$', views.TenantView.as_view(), name='tenant'),
|
||||
url(r'^tenants/(?P<slug>[\w-]+)/edit/$', views.TenantEditView.as_view(), name='tenant_edit'),
|
||||
url(r'^tenants/(?P<slug>[\w-]+)/delete/$', views.TenantDeleteView.as_view(), name='tenant_delete'),
|
||||
url(r'^tenants/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='tenant_changelog', kwargs={'model': Tenant}),
|
||||
path(r'tenants/', views.TenantListView.as_view(), name='tenant_list'),
|
||||
path(r'tenants/add/', views.TenantCreateView.as_view(), name='tenant_add'),
|
||||
path(r'tenants/import/', views.TenantBulkImportView.as_view(), name='tenant_import'),
|
||||
path(r'tenants/edit/', views.TenantBulkEditView.as_view(), name='tenant_bulk_edit'),
|
||||
path(r'tenants/delete/', views.TenantBulkDeleteView.as_view(), name='tenant_bulk_delete'),
|
||||
path(r'tenants/<slug:slug>/', views.TenantView.as_view(), name='tenant'),
|
||||
path(r'tenants/<slug:slug>/edit/', views.TenantEditView.as_view(), name='tenant_edit'),
|
||||
path(r'tenants/<slug:slug>/delete/', views.TenantDeleteView.as_view(), name='tenant_delete'),
|
||||
path(r'tenants/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='tenant_changelog', kwargs={'model': Tenant}),
|
||||
|
||||
]
|
||||
|
@ -1,18 +1,18 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'user'
|
||||
urlpatterns = [
|
||||
|
||||
url(r'^profile/$', views.ProfileView.as_view(), name='profile'),
|
||||
url(r'^password/$', views.ChangePasswordView.as_view(), name='change_password'),
|
||||
url(r'^api-tokens/$', views.TokenListView.as_view(), name='token_list'),
|
||||
url(r'^api-tokens/add/$', views.TokenEditView.as_view(), name='token_add'),
|
||||
url(r'^api-tokens/(?P<pk>\d+)/edit/$', views.TokenEditView.as_view(), name='token_edit'),
|
||||
url(r'^api-tokens/(?P<pk>\d+)/delete/$', views.TokenDeleteView.as_view(), name='token_delete'),
|
||||
url(r'^user-key/$', views.UserKeyView.as_view(), name='userkey'),
|
||||
url(r'^user-key/edit/$', views.UserKeyEditView.as_view(), name='userkey_edit'),
|
||||
url(r'^session-key/delete/$', views.SessionKeyDeleteView.as_view(), name='sessionkey_delete'),
|
||||
path(r'profile/', views.ProfileView.as_view(), name='profile'),
|
||||
path(r'password/', views.ChangePasswordView.as_view(), name='change_password'),
|
||||
path(r'api-tokens/', views.TokenListView.as_view(), name='token_list'),
|
||||
path(r'api-tokens/add/', views.TokenEditView.as_view(), name='token_add'),
|
||||
path(r'api-tokens/<int:pk>/edit/', views.TokenEditView.as_view(), name='token_edit'),
|
||||
path(r'api-tokens/<int:pk>/delete/', views.TokenDeleteView.as_view(), name='token_delete'),
|
||||
path(r'user-key/', views.UserKeyView.as_view(), name='userkey'),
|
||||
path(r'user-key/edit/', views.UserKeyEditView.as_view(), name='userkey_edit'),
|
||||
path(r'session-key/delete/', views.SessionKeyDeleteView.as_view(), name='sessionkey_delete'),
|
||||
|
||||
]
|
||||
|
@ -4,7 +4,7 @@ import pytz
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
|
||||
from django.db.models import ManyToManyField
|
||||
from django.db.models import ManyToManyField, ProtectedError
|
||||
from django.http import Http404
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import BasePermission
|
||||
@ -274,6 +274,19 @@ class ModelViewSet(_ModelViewSet):
|
||||
# Fall back to the hard-coded serializer class
|
||||
return self.serializer_class
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
except ProtectedError as e:
|
||||
models = ['{} ({})'.format(o, o._meta) for o in e.protected_objects.all()]
|
||||
msg = 'Unable to delete object. The following dependent objects were found: {}'.format(', '.join(models))
|
||||
return self.finalize_response(
|
||||
request,
|
||||
Response({'detail': msg}, status=409),
|
||||
*args,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
"""
|
||||
Call to super to allow for caching
|
||||
|
@ -2,6 +2,7 @@ from django.conf import settings
|
||||
from django.db import ProgrammingError
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
import urllib
|
||||
|
||||
from .views import server_error
|
||||
|
||||
@ -22,7 +23,12 @@ class LoginRequiredMiddleware(object):
|
||||
# performs its own authentication. Also metrics can be read without login.
|
||||
api_path = reverse('api-root')
|
||||
if not request.path_info.startswith((api_path, '/metrics')) and request.path_info != settings.LOGIN_URL:
|
||||
return HttpResponseRedirect('{}?next={}'.format(settings.LOGIN_URL, request.path_info))
|
||||
return HttpResponseRedirect(
|
||||
'{}?next={}'.format(
|
||||
settings.LOGIN_URL,
|
||||
urllib.parse.quote(request.get_full_path_info())
|
||||
)
|
||||
)
|
||||
return self.get_response(request)
|
||||
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
|
||||
from django.conf import settings
|
||||
@ -12,7 +11,7 @@ from django.forms import CharField, Form, ModelMultipleChoiceField, MultipleHidd
|
||||
from django.http import HttpResponse, HttpResponseServerError
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.template import loader
|
||||
from django.template.exceptions import TemplateDoesNotExist, TemplateSyntaxError
|
||||
from django.template.exceptions import TemplateDoesNotExist
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.http import is_safe_url
|
||||
@ -23,6 +22,7 @@ from django.views.generic import View
|
||||
from django_tables2 import RequestConfig
|
||||
|
||||
from extras.models import CustomField, CustomFieldValue, ExportTemplate
|
||||
from extras.querysets import CustomFieldQueryset
|
||||
from utilities.forms import BootstrapMixin, CSVDataField
|
||||
from utilities.utils import csv_format
|
||||
from .error_handlers import handle_protectederror
|
||||
@ -30,23 +30,6 @@ from .forms import ConfirmationForm
|
||||
from .paginator import EnhancedPaginator
|
||||
|
||||
|
||||
class CustomFieldQueryset:
|
||||
"""
|
||||
Annotate custom fields on objects within a QuerySet.
|
||||
"""
|
||||
|
||||
def __init__(self, queryset, custom_fields):
|
||||
self.queryset = queryset
|
||||
self.model = queryset.model
|
||||
self.custom_fields = custom_fields
|
||||
|
||||
def __iter__(self):
|
||||
for obj in self.queryset:
|
||||
values_dict = {cfv.field_id: cfv.value for cfv in obj.custom_field_values.all()}
|
||||
obj.custom_fields = OrderedDict([(field, values_dict.get(field.pk)) for field in self.custom_fields])
|
||||
yield obj
|
||||
|
||||
|
||||
class GetReturnURLMixin(object):
|
||||
"""
|
||||
Provides logic for determining where a user should be redirected after processing a form.
|
||||
@ -115,8 +98,9 @@ class ObjectListView(View):
|
||||
self.queryset = self.filter(request.GET, self.queryset).qs
|
||||
|
||||
# If this type of object has one or more custom fields, prefetch any relevant custom field values
|
||||
custom_fields = CustomField.objects.filter(obj_type=ContentType.objects.get_for_model(model))\
|
||||
.prefetch_related('choices')
|
||||
custom_fields = CustomField.objects.filter(
|
||||
obj_type=ContentType.objects.get_for_model(model)
|
||||
).prefetch_related('choices')
|
||||
if custom_fields:
|
||||
self.queryset = self.queryset.prefetch_related('custom_field_values')
|
||||
|
||||
@ -126,10 +110,12 @@ class ObjectListView(View):
|
||||
queryset = CustomFieldQueryset(self.queryset, custom_fields) if custom_fields else self.queryset
|
||||
try:
|
||||
return et.render_to_response(queryset)
|
||||
except TemplateSyntaxError:
|
||||
except Exception as e:
|
||||
messages.error(
|
||||
request,
|
||||
"There was an error rendering the selected export template ({}).".format(et.name)
|
||||
"There was an error rendering the selected export template ({}): {}".format(
|
||||
et.name, e
|
||||
)
|
||||
)
|
||||
|
||||
# Fall back to built-in CSV formatting if export requested but no template specified
|
||||
|
@ -5,7 +5,7 @@ from netaddr.core import AddrFormatError
|
||||
|
||||
from dcim.models import DeviceRole, Interface, Platform, Region, Site
|
||||
from extras.filters import CustomFieldFilterSet
|
||||
from tenancy.models import Tenant
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
|
||||
from .constants import VM_STATUS_CHOICES
|
||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
||||
@ -79,7 +79,7 @@ class ClusterFilter(CustomFieldFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class VirtualMachineFilter(CustomFieldFilterSet):
|
||||
class VirtualMachineFilter(TenancyFilterSet, CustomFieldFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
@ -150,16 +150,6 @@ class VirtualMachineFilter(CustomFieldFilterSet):
|
||||
to_field_name='slug',
|
||||
label='Role (slug)',
|
||||
)
|
||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Tenant.objects.all(),
|
||||
label='Tenant (ID)',
|
||||
)
|
||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='tenant__slug',
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Tenant (slug)',
|
||||
)
|
||||
platform_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Platform.objects.all(),
|
||||
label='Platform (ID)',
|
||||
|
@ -8,6 +8,7 @@ from dcim.models import Device, DeviceRole, Interface, Platform, Rack, Region, S
|
||||
from extras.forms import AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
|
||||
from ipam.models import IPAddress
|
||||
from tenancy.forms import TenancyForm
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
|
||||
@ -336,8 +337,8 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
class Meta:
|
||||
model = VirtualMachine
|
||||
fields = [
|
||||
'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6',
|
||||
'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data',
|
||||
'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4',
|
||||
'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data',
|
||||
]
|
||||
help_texts = {
|
||||
'local_context_data': "Local config context data overwrites all sources contexts in the final rendered "
|
||||
@ -520,8 +521,12 @@ class VirtualMachineBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldB
|
||||
]
|
||||
|
||||
|
||||
class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = VirtualMachine
|
||||
field_order = [
|
||||
'q', 'cluster_group', 'cluster_type', 'cluster_id', 'status', 'role', 'region', 'site', 'tenant_group',
|
||||
'tenant', 'platform',
|
||||
]
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
@ -591,16 +596,6 @@ class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
required=False,
|
||||
widget=StaticSelect2Multiple()
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.all(),
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
widget=APISelectMultiple(
|
||||
api_url='/api/tenancy/tenants/',
|
||||
value_field="slug",
|
||||
null_option=True,
|
||||
)
|
||||
)
|
||||
platform = FilterChoiceField(
|
||||
queryset=Platform.objects.all(),
|
||||
to_field_name='slug',
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from extras.views import ObjectChangeLogView
|
||||
from ipam.views import ServiceCreateView
|
||||
@ -9,53 +9,53 @@ app_name = 'virtualization'
|
||||
urlpatterns = [
|
||||
|
||||
# Cluster types
|
||||
url(r'^cluster-types/$', views.ClusterTypeListView.as_view(), name='clustertype_list'),
|
||||
url(r'^cluster-types/add/$', views.ClusterTypeCreateView.as_view(), name='clustertype_add'),
|
||||
url(r'^cluster-types/import/$', views.ClusterTypeBulkImportView.as_view(), name='clustertype_import'),
|
||||
url(r'^cluster-types/delete/$', views.ClusterTypeBulkDeleteView.as_view(), name='clustertype_bulk_delete'),
|
||||
url(r'^cluster-types/(?P<slug>[\w-]+)/edit/$', views.ClusterTypeEditView.as_view(), name='clustertype_edit'),
|
||||
url(r'^cluster-types/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='clustertype_changelog', kwargs={'model': ClusterType}),
|
||||
path(r'cluster-types/', views.ClusterTypeListView.as_view(), name='clustertype_list'),
|
||||
path(r'cluster-types/add/', views.ClusterTypeCreateView.as_view(), name='clustertype_add'),
|
||||
path(r'cluster-types/import/', views.ClusterTypeBulkImportView.as_view(), name='clustertype_import'),
|
||||
path(r'cluster-types/delete/', views.ClusterTypeBulkDeleteView.as_view(), name='clustertype_bulk_delete'),
|
||||
path(r'cluster-types/<slug:slug>/edit/', views.ClusterTypeEditView.as_view(), name='clustertype_edit'),
|
||||
path(r'cluster-types/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='clustertype_changelog', kwargs={'model': ClusterType}),
|
||||
|
||||
# Cluster groups
|
||||
url(r'^cluster-groups/$', views.ClusterGroupListView.as_view(), name='clustergroup_list'),
|
||||
url(r'^cluster-groups/add/$', views.ClusterGroupCreateView.as_view(), name='clustergroup_add'),
|
||||
url(r'^cluster-groups/import/$', views.ClusterGroupBulkImportView.as_view(), name='clustergroup_import'),
|
||||
url(r'^cluster-groups/delete/$', views.ClusterGroupBulkDeleteView.as_view(), name='clustergroup_bulk_delete'),
|
||||
url(r'^cluster-groups/(?P<slug>[\w-]+)/edit/$', views.ClusterGroupEditView.as_view(), name='clustergroup_edit'),
|
||||
url(r'^cluster-groups/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='clustergroup_changelog', kwargs={'model': ClusterGroup}),
|
||||
path(r'cluster-groups/', views.ClusterGroupListView.as_view(), name='clustergroup_list'),
|
||||
path(r'cluster-groups/add/', views.ClusterGroupCreateView.as_view(), name='clustergroup_add'),
|
||||
path(r'cluster-groups/import/', views.ClusterGroupBulkImportView.as_view(), name='clustergroup_import'),
|
||||
path(r'cluster-groups/delete/', views.ClusterGroupBulkDeleteView.as_view(), name='clustergroup_bulk_delete'),
|
||||
path(r'cluster-groups/<slug:slug>/edit/', views.ClusterGroupEditView.as_view(), name='clustergroup_edit'),
|
||||
path(r'cluster-groups/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='clustergroup_changelog', kwargs={'model': ClusterGroup}),
|
||||
|
||||
# Clusters
|
||||
url(r'^clusters/$', views.ClusterListView.as_view(), name='cluster_list'),
|
||||
url(r'^clusters/add/$', views.ClusterCreateView.as_view(), name='cluster_add'),
|
||||
url(r'^clusters/import/$', views.ClusterBulkImportView.as_view(), name='cluster_import'),
|
||||
url(r'^clusters/edit/$', views.ClusterBulkEditView.as_view(), name='cluster_bulk_edit'),
|
||||
url(r'^clusters/delete/$', views.ClusterBulkDeleteView.as_view(), name='cluster_bulk_delete'),
|
||||
url(r'^clusters/(?P<pk>\d+)/$', views.ClusterView.as_view(), name='cluster'),
|
||||
url(r'^clusters/(?P<pk>\d+)/edit/$', views.ClusterEditView.as_view(), name='cluster_edit'),
|
||||
url(r'^clusters/(?P<pk>\d+)/delete/$', views.ClusterDeleteView.as_view(), name='cluster_delete'),
|
||||
url(r'^clusters/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='cluster_changelog', kwargs={'model': Cluster}),
|
||||
url(r'^clusters/(?P<pk>\d+)/devices/add/$', views.ClusterAddDevicesView.as_view(), name='cluster_add_devices'),
|
||||
url(r'^clusters/(?P<pk>\d+)/devices/remove/$', views.ClusterRemoveDevicesView.as_view(), name='cluster_remove_devices'),
|
||||
path(r'clusters/', views.ClusterListView.as_view(), name='cluster_list'),
|
||||
path(r'clusters/add/', views.ClusterCreateView.as_view(), name='cluster_add'),
|
||||
path(r'clusters/import/', views.ClusterBulkImportView.as_view(), name='cluster_import'),
|
||||
path(r'clusters/edit/', views.ClusterBulkEditView.as_view(), name='cluster_bulk_edit'),
|
||||
path(r'clusters/delete/', views.ClusterBulkDeleteView.as_view(), name='cluster_bulk_delete'),
|
||||
path(r'clusters/<int:pk>/', views.ClusterView.as_view(), name='cluster'),
|
||||
path(r'clusters/<int:pk>/edit/', views.ClusterEditView.as_view(), name='cluster_edit'),
|
||||
path(r'clusters/<int:pk>/delete/', views.ClusterDeleteView.as_view(), name='cluster_delete'),
|
||||
path(r'clusters/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cluster_changelog', kwargs={'model': Cluster}),
|
||||
path(r'clusters/<int:pk>/devices/add/', views.ClusterAddDevicesView.as_view(), name='cluster_add_devices'),
|
||||
path(r'clusters/<int:pk>/devices/remove/', views.ClusterRemoveDevicesView.as_view(), name='cluster_remove_devices'),
|
||||
|
||||
# Virtual machines
|
||||
url(r'^virtual-machines/$', views.VirtualMachineListView.as_view(), name='virtualmachine_list'),
|
||||
url(r'^virtual-machines/add/$', views.VirtualMachineCreateView.as_view(), name='virtualmachine_add'),
|
||||
url(r'^virtual-machines/import/$', views.VirtualMachineBulkImportView.as_view(), name='virtualmachine_import'),
|
||||
url(r'^virtual-machines/edit/$', views.VirtualMachineBulkEditView.as_view(), name='virtualmachine_bulk_edit'),
|
||||
url(r'^virtual-machines/delete/$', views.VirtualMachineBulkDeleteView.as_view(), name='virtualmachine_bulk_delete'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/$', views.VirtualMachineView.as_view(), name='virtualmachine'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/edit/$', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/delete/$', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/config-context/$', views.VirtualMachineConfigContextView.as_view(), name='virtualmachine_configcontext'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='virtualmachine_changelog', kwargs={'model': VirtualMachine}),
|
||||
url(r'^virtual-machines/(?P<virtualmachine>\d+)/services/assign/$', ServiceCreateView.as_view(), name='virtualmachine_service_assign'),
|
||||
path(r'virtual-machines/', views.VirtualMachineListView.as_view(), name='virtualmachine_list'),
|
||||
path(r'virtual-machines/add/', views.VirtualMachineCreateView.as_view(), name='virtualmachine_add'),
|
||||
path(r'virtual-machines/import/', views.VirtualMachineBulkImportView.as_view(), name='virtualmachine_import'),
|
||||
path(r'virtual-machines/edit/', views.VirtualMachineBulkEditView.as_view(), name='virtualmachine_bulk_edit'),
|
||||
path(r'virtual-machines/delete/', views.VirtualMachineBulkDeleteView.as_view(), name='virtualmachine_bulk_delete'),
|
||||
path(r'virtual-machines/<int:pk>/', views.VirtualMachineView.as_view(), name='virtualmachine'),
|
||||
path(r'virtual-machines/<int:pk>/edit/', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
|
||||
path(r'virtual-machines/<int:pk>/delete/', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
|
||||
path(r'virtual-machines/<int:pk>/config-context/', views.VirtualMachineConfigContextView.as_view(), name='virtualmachine_configcontext'),
|
||||
path(r'virtual-machines/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualmachine_changelog', kwargs={'model': VirtualMachine}),
|
||||
path(r'virtual-machines/<int:virtualmachine>/services/assign/', ServiceCreateView.as_view(), name='virtualmachine_service_assign'),
|
||||
|
||||
# VM interfaces
|
||||
url(r'^virtual-machines/interfaces/add/$', views.VirtualMachineBulkAddInterfaceView.as_view(), name='virtualmachine_bulk_add_interface'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/interfaces/add/$', views.InterfaceCreateView.as_view(), name='interface_add'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||
url(r'^virtual-machines/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||
url(r'^vm-interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||
url(r'^vm-interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||
path(r'virtual-machines/interfaces/add/', views.VirtualMachineBulkAddInterfaceView.as_view(), name='virtualmachine_bulk_add_interface'),
|
||||
path(r'virtual-machines/<int:pk>/interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
|
||||
path(r'virtual-machines/<int:pk>/interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||
path(r'virtual-machines/<int:pk>/interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||
path(r'vm-interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||
path(r'vm-interfaces/<int:pk>/delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||
|
||||
]
|
||||
|
@ -10,8 +10,13 @@ django-tables2==2.0.6
|
||||
django-taggit==1.1.0
|
||||
django-taggit-serializer==0.1.7
|
||||
django-timezone-field==3.0
|
||||
<<<<<<< HEAD
|
||||
djangorestframework==3.9.2
|
||||
drf-yasg[validation]==1.15.0
|
||||
=======
|
||||
djangorestframework==3.9.1
|
||||
drf-yasg[validation]==1.14.0
|
||||
>>>>>>> develop
|
||||
graphviz==0.10.1
|
||||
Jinja2==2.10.1
|
||||
Markdown==2.6.11
|
||||
|
@ -5,6 +5,8 @@
|
||||
# Once the script completes, remember to restart the WSGI service (e.g.
|
||||
# gunicorn or uWSGI).
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
PYTHON="python3"
|
||||
PIP="pip3"
|
||||
|
||||
|
Reference in New Issue
Block a user