mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
merge develop
This commit is contained in:
@ -22,6 +22,10 @@ django-filter
|
|||||||
# https://github.com/django-mptt/django-mptt
|
# https://github.com/django-mptt/django-mptt
|
||||||
django-mptt
|
django-mptt
|
||||||
|
|
||||||
|
# Context managers for PostgreSQL advisory locks
|
||||||
|
# https://github.com/Xof/django-pglocks
|
||||||
|
django-pglocks
|
||||||
|
|
||||||
# Prometheus metrics library for Django
|
# Prometheus metrics library for Django
|
||||||
# https://github.com/korfuri/django-prometheus
|
# https://github.com/korfuri/django-prometheus
|
||||||
django-prometheus
|
django-prometheus
|
||||||
|
@ -80,11 +80,11 @@ REDIS = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note:
|
!!! note
|
||||||
If you are upgrading from a version prior to v2.7, please note that the Redis connection configuration settings have
|
If you are upgrading from a version prior to v2.7, please note that the Redis connection configuration settings have
|
||||||
changed. Manual modification to bring the `REDIS` section inline with the above specification is necessary
|
changed. Manual modification to bring the `REDIS` section inline with the above specification is necessary
|
||||||
|
|
||||||
!!! warning:
|
!!! note
|
||||||
It is highly recommended to keep the webhook and cache databases separate. Using the same database number on the
|
It is highly recommended to keep the webhook and cache databases separate. Using the same database number on the
|
||||||
same Redis instance for both may result in webhook processing data being lost during cache flushing events.
|
same Redis instance for both may result in webhook processing data being lost during cache flushing events.
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ REDIS = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note:
|
!!! note
|
||||||
It is possible to have only one or the other Redis configurations to use Sentinel functionality. It is possible
|
It is possible to have only one or the other Redis configurations to use Sentinel functionality. It is possible
|
||||||
for example to have the webhook use sentinel via `HOST`/`PORT` and for caching to use Sentinel via
|
for example to have the webhook use sentinel via `HOST`/`PORT` and for caching to use Sentinel via
|
||||||
`SENTINELS`/`SENTINEL_SERVICE`.
|
`SENTINELS`/`SENTINEL_SERVICE`.
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
# v2.7.7 (FUTURE)
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
* [#3840](https://github.com/netbox-community/netbox/issues/3840) - Enhance search function when selecting VLANs for interface assignment
|
||||||
|
* [#4170](https://github.com/netbox-community/netbox/issues/4170) - Improve color contrast in rack elevation drawings
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [#2519](https://github.com/netbox-community/netbox/issues/2519) - Avoid race condition when provisioning "next available" IPs/prefixes via the API
|
||||||
|
* [#4168](https://github.com/netbox-community/netbox/issues/4168) - Role is not required when creating a virtual machine
|
||||||
|
* [#4175](https://github.com/netbox-community/netbox/issues/4175) - Fix potential exception when bulk editing objects from a filtered list
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# v2.7.6 (2020-02-13)
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [#4166](https://github.com/netbox-community/netbox/issues/4166) - Fix schema migrations to enforce maximum character length for naturalized fields
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# v2.7.5 (2020-02-13)
|
# v2.7.5 (2020-02-13)
|
||||||
|
|
||||||
**Note:** This release includes several database schema migrations that calculate and store copies of names for certain objects to improve natural ordering performance (see [#3799](https://github.com/netbox-community/netbox/issues/3799)). These migrations may take a few minutes to run if you have a very large number of objects defined in NetBox.
|
**Note:** This release includes several database schema migrations that calculate and store copies of names for certain objects to improve natural ordering performance (see [#3799](https://github.com/netbox-community/netbox/issues/3799)). These migrations may take a few minutes to run if you have a very large number of objects defined in NetBox.
|
||||||
|
@ -29,7 +29,6 @@ class ProviderListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.ProviderFilterSet
|
filterset = filters.ProviderFilterSet
|
||||||
filterset_form = forms.ProviderFilterForm
|
filterset_form = forms.ProviderFilterForm
|
||||||
table = tables.ProviderDetailTable
|
table = tables.ProviderDetailTable
|
||||||
template_name = 'circuits/provider_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class ProviderView(PermissionRequiredMixin, View):
|
class ProviderView(PermissionRequiredMixin, View):
|
||||||
@ -107,7 +106,6 @@ class CircuitTypeListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'circuits.view_circuittype'
|
permission_required = 'circuits.view_circuittype'
|
||||||
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
|
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
|
||||||
table = tables.CircuitTypeTable
|
table = tables.CircuitTypeTable
|
||||||
template_name = 'circuits/circuittype_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeCreateView(PermissionRequiredMixin, ObjectEditView):
|
class CircuitTypeCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -151,7 +149,6 @@ class CircuitListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.CircuitFilterSet
|
filterset = filters.CircuitFilterSet
|
||||||
filterset_form = forms.CircuitFilterForm
|
filterset_form = forms.CircuitFilterForm
|
||||||
table = tables.CircuitTable
|
table = tables.CircuitTable
|
||||||
template_name = 'circuits/circuit_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class CircuitView(PermissionRequiredMixin, View):
|
class CircuitView(PermissionRequiredMixin, View):
|
||||||
|
@ -2832,7 +2832,10 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
|
|||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
@ -2842,7 +2845,10 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
@ -2871,18 +2877,20 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Limit LAG choices to interfaces belonging to this device (or VC master)
|
|
||||||
if self.is_bound:
|
if self.is_bound:
|
||||||
device = Device.objects.get(pk=self.data['device'])
|
device = Device.objects.get(pk=self.data['device'])
|
||||||
self.fields['lag'].queryset = Interface.objects.filter(
|
|
||||||
device__in=[device, device.get_vc_master()],
|
|
||||||
type=InterfaceTypeChoices.TYPE_LAG
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.fields['lag'].queryset = Interface.objects.filter(
|
device = self.instance.device
|
||||||
device__in=[self.instance.device, self.instance.device.get_vc_master()],
|
|
||||||
type=InterfaceTypeChoices.TYPE_LAG
|
# Limit LAG choices to interfaces belonging to this device (or VC master)
|
||||||
)
|
self.fields['lag'].queryset = Interface.objects.filter(
|
||||||
|
device__in=[device, device.get_vc_master()],
|
||||||
|
type=InterfaceTypeChoices.TYPE_LAG
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add current site to VLANs query params
|
||||||
|
self.fields['untagged_vlan'].widget.add_additional_query_param('site_id', device.site.pk)
|
||||||
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', device.site.pk)
|
||||||
|
|
||||||
|
|
||||||
class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form):
|
class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form):
|
||||||
@ -2942,7 +2950,10 @@ class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form):
|
|||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
@ -2951,7 +2962,10 @@ class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2967,6 +2981,10 @@ class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form):
|
|||||||
type=InterfaceTypeChoices.TYPE_LAG
|
type=InterfaceTypeChoices.TYPE_LAG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add current site to VLANs query params
|
||||||
|
self.fields['untagged_vlan'].widget.add_additional_query_param('site_id', device.site.pk)
|
||||||
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', device.site.pk)
|
||||||
|
|
||||||
|
|
||||||
class InterfaceCSVForm(forms.ModelForm):
|
class InterfaceCSVForm(forms.ModelForm):
|
||||||
device = FlexibleModelChoiceField(
|
device = FlexibleModelChoiceField(
|
||||||
@ -3090,7 +3108,10 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
|||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
@ -3099,7 +3120,10 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -3118,6 +3142,10 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
|||||||
device__in=[device, device.get_vc_master()],
|
device__in=[device, device.get_vc_master()],
|
||||||
type=InterfaceTypeChoices.TYPE_LAG
|
type=InterfaceTypeChoices.TYPE_LAG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add current site to VLANs query params
|
||||||
|
self.fields['untagged_vlan'].widget.add_additional_query_param('site_id', device.site.pk)
|
||||||
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', device.site.pk)
|
||||||
else:
|
else:
|
||||||
self.fields['lag'].choices = ()
|
self.fields['lag'].choices = ()
|
||||||
self.fields['lag'].widget.attrs['disabled'] = True
|
self.fields['lag'].widget.attrs['disabled'] = True
|
||||||
|
@ -6,7 +6,7 @@ import utilities.ordering
|
|||||||
def _update_model_names(model):
|
def _update_model_names(model):
|
||||||
# Update each unique field value in bulk
|
# Update each unique field value in bulk
|
||||||
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
||||||
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name))
|
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
def naturalize_consoleports(apps, schema_editor):
|
def naturalize_consoleports(apps, schema_editor):
|
||||||
|
@ -6,7 +6,7 @@ import utilities.ordering
|
|||||||
def _update_model_names(model):
|
def _update_model_names(model):
|
||||||
# Update each unique field value in bulk
|
# Update each unique field value in bulk
|
||||||
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
||||||
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name))
|
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
def naturalize_consoleporttemplates(apps, schema_editor):
|
def naturalize_consoleporttemplates(apps, schema_editor):
|
||||||
|
@ -6,7 +6,7 @@ import utilities.ordering
|
|||||||
def _update_model_names(model):
|
def _update_model_names(model):
|
||||||
# Update each unique field value in bulk
|
# Update each unique field value in bulk
|
||||||
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
||||||
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name))
|
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize(name, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
def naturalize_sites(apps, schema_editor):
|
def naturalize_sites(apps, schema_editor):
|
||||||
|
@ -6,7 +6,7 @@ import utilities.ordering
|
|||||||
def _update_model_names(model):
|
def _update_model_names(model):
|
||||||
# Update each unique field value in bulk
|
# Update each unique field value in bulk
|
||||||
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
for name in model.objects.values_list('name', flat=True).order_by('name').distinct():
|
||||||
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize_interface(name))
|
model.objects.filter(name=name).update(_name=utilities.ordering.naturalize_interface(name, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
def naturalize_interfacetemplates(apps, schema_editor):
|
def naturalize_interfacetemplates(apps, schema_editor):
|
||||||
|
@ -382,8 +382,8 @@ class RackElevationHelperMixin:
|
|||||||
|
|
||||||
# add gradients
|
# add gradients
|
||||||
RackElevationHelperMixin._add_gradient(drawing, 'reserved', '#c7c7ff')
|
RackElevationHelperMixin._add_gradient(drawing, 'reserved', '#c7c7ff')
|
||||||
RackElevationHelperMixin._add_gradient(drawing, 'occupied', '#f0f0f0')
|
RackElevationHelperMixin._add_gradient(drawing, 'occupied', '#d7d7d7')
|
||||||
RackElevationHelperMixin._add_gradient(drawing, 'blocked', '#ffc7c7')
|
RackElevationHelperMixin._add_gradient(drawing, 'blocked', '#ffc0c0')
|
||||||
|
|
||||||
return drawing
|
return drawing
|
||||||
|
|
||||||
|
@ -152,7 +152,6 @@ class RegionListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.RegionFilterSet
|
filterset = filters.RegionFilterSet
|
||||||
filterset_form = forms.RegionFilterForm
|
filterset_form = forms.RegionFilterForm
|
||||||
table = tables.RegionTable
|
table = tables.RegionTable
|
||||||
template_name = 'dcim/region_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class RegionCreateView(PermissionRequiredMixin, ObjectEditView):
|
class RegionCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -191,7 +190,6 @@ class SiteListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.SiteFilterSet
|
filterset = filters.SiteFilterSet
|
||||||
filterset_form = forms.SiteFilterForm
|
filterset_form = forms.SiteFilterForm
|
||||||
table = tables.SiteTable
|
table = tables.SiteTable
|
||||||
template_name = 'dcim/site_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class SiteView(PermissionRequiredMixin, View):
|
class SiteView(PermissionRequiredMixin, View):
|
||||||
@ -271,7 +269,6 @@ class RackGroupListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.RackGroupFilterSet
|
filterset = filters.RackGroupFilterSet
|
||||||
filterset_form = forms.RackGroupFilterForm
|
filterset_form = forms.RackGroupFilterForm
|
||||||
table = tables.RackGroupTable
|
table = tables.RackGroupTable
|
||||||
template_name = 'dcim/rackgroup_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class RackGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
class RackGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -308,7 +305,6 @@ class RackRoleListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'dcim.view_rackrole'
|
permission_required = 'dcim.view_rackrole'
|
||||||
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
|
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
|
||||||
table = tables.RackRoleTable
|
table = tables.RackRoleTable
|
||||||
template_name = 'dcim/rackrole_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class RackRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
class RackRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -350,7 +346,6 @@ class RackListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.RackFilterSet
|
filterset = filters.RackFilterSet
|
||||||
filterset_form = forms.RackFilterForm
|
filterset_form = forms.RackFilterForm
|
||||||
table = tables.RackDetailTable
|
table = tables.RackDetailTable
|
||||||
template_name = 'dcim/rack_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class RackElevationListView(PermissionRequiredMixin, View):
|
class RackElevationListView(PermissionRequiredMixin, View):
|
||||||
@ -474,7 +469,7 @@ class RackReservationListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.RackReservationFilterSet
|
filterset = filters.RackReservationFilterSet
|
||||||
filterset_form = forms.RackReservationFilterForm
|
filterset_form = forms.RackReservationFilterForm
|
||||||
table = tables.RackReservationTable
|
table = tables.RackReservationTable
|
||||||
template_name = 'dcim/rackreservation_list.html'
|
action_buttons = ()
|
||||||
|
|
||||||
|
|
||||||
class RackReservationCreateView(PermissionRequiredMixin, ObjectEditView):
|
class RackReservationCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -533,7 +528,6 @@ class ManufacturerListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
platform_count=Count('platforms', distinct=True),
|
platform_count=Count('platforms', distinct=True),
|
||||||
)
|
)
|
||||||
table = tables.ManufacturerTable
|
table = tables.ManufacturerTable
|
||||||
template_name = 'dcim/manufacturer_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerCreateView(PermissionRequiredMixin, ObjectEditView):
|
class ManufacturerCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -571,7 +565,6 @@ class DeviceTypeListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.DeviceTypeFilterSet
|
filterset = filters.DeviceTypeFilterSet
|
||||||
filterset_form = forms.DeviceTypeFilterForm
|
filterset_form = forms.DeviceTypeFilterForm
|
||||||
table = tables.DeviceTypeTable
|
table = tables.DeviceTypeTable
|
||||||
template_name = 'dcim/devicetype_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeView(PermissionRequiredMixin, View):
|
class DeviceTypeView(PermissionRequiredMixin, View):
|
||||||
@ -995,7 +988,6 @@ class DeviceRoleListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'dcim.view_devicerole'
|
permission_required = 'dcim.view_devicerole'
|
||||||
queryset = DeviceRole.objects.all()
|
queryset = DeviceRole.objects.all()
|
||||||
table = tables.DeviceRoleTable
|
table = tables.DeviceRoleTable
|
||||||
template_name = 'dcim/devicerole_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
class DeviceRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -1031,7 +1023,6 @@ class PlatformListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'dcim.view_platform'
|
permission_required = 'dcim.view_platform'
|
||||||
queryset = Platform.objects.all()
|
queryset = Platform.objects.all()
|
||||||
table = tables.PlatformTable
|
table = tables.PlatformTable
|
||||||
template_name = 'dcim/platform_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class PlatformCreateView(PermissionRequiredMixin, ObjectEditView):
|
class PlatformCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -1292,7 +1283,7 @@ class ConsolePortListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.ConsolePortFilterSet
|
filterset = filters.ConsolePortFilterSet
|
||||||
filterset_form = forms.ConsolePortFilterForm
|
filterset_form = forms.ConsolePortFilterForm
|
||||||
table = tables.ConsolePortDetailTable
|
table = tables.ConsolePortDetailTable
|
||||||
template_name = 'dcim/consoleport_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class ConsolePortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1345,7 +1336,7 @@ class ConsoleServerPortListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.ConsoleServerPortFilterSet
|
filterset = filters.ConsoleServerPortFilterSet
|
||||||
filterset_form = forms.ConsoleServerPortFilterForm
|
filterset_form = forms.ConsoleServerPortFilterForm
|
||||||
table = tables.ConsoleServerPortDetailTable
|
table = tables.ConsoleServerPortDetailTable
|
||||||
template_name = 'dcim/consoleserverport_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class ConsoleServerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1410,7 +1401,7 @@ class PowerPortListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.PowerPortFilterSet
|
filterset = filters.PowerPortFilterSet
|
||||||
filterset_form = forms.PowerPortFilterForm
|
filterset_form = forms.PowerPortFilterForm
|
||||||
table = tables.PowerPortDetailTable
|
table = tables.PowerPortDetailTable
|
||||||
template_name = 'dcim/powerport_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class PowerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class PowerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1463,7 +1454,7 @@ class PowerOutletListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.PowerOutletFilterSet
|
filterset = filters.PowerOutletFilterSet
|
||||||
filterset_form = forms.PowerOutletFilterForm
|
filterset_form = forms.PowerOutletFilterForm
|
||||||
table = tables.PowerOutletDetailTable
|
table = tables.PowerOutletDetailTable
|
||||||
template_name = 'dcim/poweroutlet_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class PowerOutletCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1528,7 +1519,7 @@ class InterfaceListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.InterfaceFilterSet
|
filterset = filters.InterfaceFilterSet
|
||||||
filterset_form = forms.InterfaceFilterForm
|
filterset_form = forms.InterfaceFilterForm
|
||||||
table = tables.InterfaceDetailTable
|
table = tables.InterfaceDetailTable
|
||||||
template_name = 'dcim/interface_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class InterfaceView(PermissionRequiredMixin, View):
|
class InterfaceView(PermissionRequiredMixin, View):
|
||||||
@ -1630,7 +1621,7 @@ class FrontPortListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.FrontPortFilterSet
|
filterset = filters.FrontPortFilterSet
|
||||||
filterset_form = forms.FrontPortFilterForm
|
filterset_form = forms.FrontPortFilterForm
|
||||||
table = tables.FrontPortDetailTable
|
table = tables.FrontPortDetailTable
|
||||||
template_name = 'dcim/frontport_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class FrontPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class FrontPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1695,7 +1686,7 @@ class RearPortListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.RearPortFilterSet
|
filterset = filters.RearPortFilterSet
|
||||||
filterset_form = forms.RearPortFilterForm
|
filterset_form = forms.RearPortFilterForm
|
||||||
table = tables.RearPortDetailTable
|
table = tables.RearPortDetailTable
|
||||||
template_name = 'dcim/rearport_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class RearPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class RearPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1762,7 +1753,7 @@ class DeviceBayListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.DeviceBayFilterSet
|
filterset = filters.DeviceBayFilterSet
|
||||||
filterset_form = forms.DeviceBayFilterForm
|
filterset_form = forms.DeviceBayFilterForm
|
||||||
table = tables.DeviceBayDetailTable
|
table = tables.DeviceBayDetailTable
|
||||||
template_name = 'dcim/devicebay_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class DeviceBayCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
@ -1961,7 +1952,7 @@ class CableListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.CableFilterSet
|
filterset = filters.CableFilterSet
|
||||||
filterset_form = forms.CableFilterForm
|
filterset_form = forms.CableFilterForm
|
||||||
table = tables.CableTable
|
table = tables.CableTable
|
||||||
template_name = 'dcim/cable_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class CableView(PermissionRequiredMixin, View):
|
class CableView(PermissionRequiredMixin, View):
|
||||||
@ -2233,7 +2224,7 @@ class InventoryItemListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.InventoryItemFilterSet
|
filterset = filters.InventoryItemFilterSet
|
||||||
filterset_form = forms.InventoryItemFilterForm
|
filterset_form = forms.InventoryItemFilterForm
|
||||||
table = tables.InventoryItemTable
|
table = tables.InventoryItemTable
|
||||||
template_name = 'dcim/inventoryitem_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemEditView(PermissionRequiredMixin, ObjectEditView):
|
class InventoryItemEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -2289,7 +2280,7 @@ class VirtualChassisListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
table = tables.VirtualChassisTable
|
table = tables.VirtualChassisTable
|
||||||
filterset = filters.VirtualChassisFilterSet
|
filterset = filters.VirtualChassisFilterSet
|
||||||
filterset_form = forms.VirtualChassisFilterForm
|
filterset_form = forms.VirtualChassisFilterForm
|
||||||
template_name = 'dcim/virtualchassis_list.html'
|
action_buttons = ('export',)
|
||||||
|
|
||||||
|
|
||||||
class VirtualChassisCreateView(PermissionRequiredMixin, View):
|
class VirtualChassisCreateView(PermissionRequiredMixin, View):
|
||||||
@ -2533,7 +2524,6 @@ class PowerPanelListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.PowerPanelFilterSet
|
filterset = filters.PowerPanelFilterSet
|
||||||
filterset_form = forms.PowerPanelFilterForm
|
filterset_form = forms.PowerPanelFilterForm
|
||||||
table = tables.PowerPanelTable
|
table = tables.PowerPanelTable
|
||||||
template_name = 'dcim/powerpanel_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelView(PermissionRequiredMixin, View):
|
class PowerPanelView(PermissionRequiredMixin, View):
|
||||||
@ -2602,7 +2592,6 @@ class PowerFeedListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.PowerFeedFilterSet
|
filterset = filters.PowerFeedFilterSet
|
||||||
filterset_form = forms.PowerFeedFilterForm
|
filterset_form = forms.PowerFeedFilterForm
|
||||||
table = tables.PowerFeedTable
|
table = tables.PowerFeedTable
|
||||||
template_name = 'dcim/powerfeed_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedView(PermissionRequiredMixin, View):
|
class PowerFeedView(PermissionRequiredMixin, View):
|
||||||
|
@ -1,28 +1,8 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.conf import settings
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
import redis
|
|
||||||
|
|
||||||
|
|
||||||
class ExtrasConfig(AppConfig):
|
class ExtrasConfig(AppConfig):
|
||||||
name = "extras"
|
name = "extras"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|
||||||
import extras.signals
|
import extras.signals
|
||||||
|
|
||||||
# Check that we can connect to the configured Redis database.
|
|
||||||
try:
|
|
||||||
rs = redis.Redis(
|
|
||||||
host=settings.WEBHOOKS_REDIS_HOST,
|
|
||||||
port=settings.WEBHOOKS_REDIS_PORT,
|
|
||||||
db=settings.WEBHOOKS_REDIS_DATABASE,
|
|
||||||
password=settings.WEBHOOKS_REDIS_PASSWORD or None,
|
|
||||||
ssl=settings.WEBHOOKS_REDIS_SSL,
|
|
||||||
)
|
|
||||||
rs.ping()
|
|
||||||
except redis.exceptions.ConnectionError:
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
"Unable to connect to the Redis database. Check that the Redis configuration has been defined in "
|
|
||||||
"configuration.py."
|
|
||||||
)
|
|
||||||
|
@ -86,7 +86,7 @@ class Command(BaseCommand):
|
|||||||
# Find all unique values for the field
|
# Find all unique values for the field
|
||||||
queryset = model.objects.values_list(target_field, flat=True).order_by(target_field).distinct()
|
queryset = model.objects.values_list(target_field, flat=True).order_by(target_field).distinct()
|
||||||
for value in queryset:
|
for value in queryset:
|
||||||
naturalized_value = naturalize(value)
|
naturalized_value = naturalize(value, max_length=field.max_length)
|
||||||
|
|
||||||
if options['verbosity'] >= 2:
|
if options['verbosity'] >= 2:
|
||||||
self.stdout.write(" {} -> {}".format(value, naturalized_value), ending='')
|
self.stdout.write(" {} -> {}".format(value, naturalized_value), ending='')
|
||||||
|
@ -34,7 +34,7 @@ class TagListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.TagFilterSet
|
filterset = filters.TagFilterSet
|
||||||
filterset_form = forms.TagFilterForm
|
filterset_form = forms.TagFilterForm
|
||||||
table = TagTable
|
table = TagTable
|
||||||
template_name = 'extras/tag_list.html'
|
action_buttons = ()
|
||||||
|
|
||||||
|
|
||||||
class TagView(PermissionRequiredMixin, View):
|
class TagView(PermissionRequiredMixin, View):
|
||||||
@ -111,7 +111,7 @@ class ConfigContextListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.ConfigContextFilterSet
|
filterset = filters.ConfigContextFilterSet
|
||||||
filterset_form = forms.ConfigContextFilterForm
|
filterset_form = forms.ConfigContextFilterForm
|
||||||
table = ConfigContextTable
|
table = ConfigContextTable
|
||||||
template_name = 'extras/configcontext_list.html'
|
action_buttons = ('add',)
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextView(PermissionRequiredMixin, View):
|
class ConfigContextView(PermissionRequiredMixin, View):
|
||||||
@ -191,6 +191,7 @@ class ObjectChangeListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset_form = forms.ObjectChangeFilterForm
|
filterset_form = forms.ObjectChangeFilterForm
|
||||||
table = ObjectChangeTable
|
table = ObjectChangeTable
|
||||||
template_name = 'extras/objectchange_list.html'
|
template_name = 'extras/objectchange_list.html'
|
||||||
|
action_buttons = ('export',)
|
||||||
|
|
||||||
|
|
||||||
class ObjectChangeView(PermissionRequiredMixin, View):
|
class ObjectChangeView(PermissionRequiredMixin, View):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django_pglocks import advisory_lock
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
@ -10,6 +11,7 @@ from extras.api.views import CustomFieldModelViewSet
|
|||||||
from ipam import filters
|
from ipam import filters
|
||||||
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
||||||
from utilities.api import FieldChoicesViewSet, ModelViewSet
|
from utilities.api import FieldChoicesViewSet, ModelViewSet
|
||||||
|
from utilities.constants import ADVISORY_LOCK_KEYS
|
||||||
from utilities.utils import get_subquery
|
from utilities.utils import get_subquery
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
@ -86,9 +88,13 @@ class PrefixViewSet(CustomFieldModelViewSet):
|
|||||||
filterset_class = filters.PrefixFilterSet
|
filterset_class = filters.PrefixFilterSet
|
||||||
|
|
||||||
@action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
|
@action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
|
||||||
|
@advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])
|
||||||
def available_prefixes(self, request, pk=None):
|
def available_prefixes(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
A convenience method for returning available child prefixes within a parent.
|
A convenience method for returning available child prefixes within a parent.
|
||||||
|
|
||||||
|
The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
|
||||||
|
invoked in parallel, which results in a race condition where multiple insertions can occur.
|
||||||
"""
|
"""
|
||||||
prefix = get_object_or_404(Prefix, pk=pk)
|
prefix = get_object_or_404(Prefix, pk=pk)
|
||||||
available_prefixes = prefix.get_available_prefixes()
|
available_prefixes = prefix.get_available_prefixes()
|
||||||
@ -180,11 +186,15 @@ class PrefixViewSet(CustomFieldModelViewSet):
|
|||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=True, url_path='available-ips', methods=['get', 'post'])
|
@action(detail=True, url_path='available-ips', methods=['get', 'post'])
|
||||||
|
@advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])
|
||||||
def available_ips(self, request, pk=None):
|
def available_ips(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
|
A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
|
||||||
returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
|
returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
|
||||||
however results will not be paginated.
|
however results will not be paginated.
|
||||||
|
|
||||||
|
The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
|
||||||
|
invoked in parallel, which results in a race condition where multiple insertions can occur.
|
||||||
"""
|
"""
|
||||||
prefix = get_object_or_404(Prefix, pk=pk)
|
prefix = get_object_or_404(Prefix, pk=pk)
|
||||||
|
|
||||||
|
@ -118,7 +118,6 @@ class VRFListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.VRFFilterSet
|
filterset = filters.VRFFilterSet
|
||||||
filterset_form = forms.VRFFilterForm
|
filterset_form = forms.VRFFilterForm
|
||||||
table = tables.VRFTable
|
table = tables.VRFTable
|
||||||
template_name = 'ipam/vrf_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class VRFView(PermissionRequiredMixin, View):
|
class VRFView(PermissionRequiredMixin, View):
|
||||||
@ -293,7 +292,6 @@ class AggregateListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
queryset = Aggregate.objects.prefetch_related('rir').annotate(
|
queryset = Aggregate.objects.prefetch_related('rir').annotate(
|
||||||
child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
|
child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
|
||||||
)
|
)
|
||||||
|
|
||||||
filterset = filters.AggregateFilterSet
|
filterset = filters.AggregateFilterSet
|
||||||
filterset_form = forms.AggregateFilterForm
|
filterset_form = forms.AggregateFilterForm
|
||||||
table = tables.AggregateDetailTable
|
table = tables.AggregateDetailTable
|
||||||
@ -411,7 +409,6 @@ class RoleListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'ipam.view_role'
|
permission_required = 'ipam.view_role'
|
||||||
queryset = Role.objects.all()
|
queryset = Role.objects.all()
|
||||||
table = tables.RoleTable
|
table = tables.RoleTable
|
||||||
template_name = 'ipam/role_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class RoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
class RoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -644,7 +641,6 @@ class IPAddressListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.IPAddressFilterSet
|
filterset = filters.IPAddressFilterSet
|
||||||
filterset_form = forms.IPAddressFilterForm
|
filterset_form = forms.IPAddressFilterForm
|
||||||
table = tables.IPAddressDetailTable
|
table = tables.IPAddressDetailTable
|
||||||
template_name = 'ipam/ipaddress_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class IPAddressView(PermissionRequiredMixin, View):
|
class IPAddressView(PermissionRequiredMixin, View):
|
||||||
@ -817,7 +813,6 @@ class VLANGroupListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.VLANGroupFilterSet
|
filterset = filters.VLANGroupFilterSet
|
||||||
filterset_form = forms.VLANGroupFilterForm
|
filterset_form = forms.VLANGroupFilterForm
|
||||||
table = tables.VLANGroupTable
|
table = tables.VLANGroupTable
|
||||||
template_name = 'ipam/vlangroup_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
class VLANGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -893,7 +888,6 @@ class VLANListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.VLANFilterSet
|
filterset = filters.VLANFilterSet
|
||||||
filterset_form = forms.VLANFilterForm
|
filterset_form = forms.VLANFilterForm
|
||||||
table = tables.VLANDetailTable
|
table = tables.VLANDetailTable
|
||||||
template_name = 'ipam/vlan_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class VLANView(PermissionRequiredMixin, View):
|
class VLANView(PermissionRequiredMixin, View):
|
||||||
@ -989,7 +983,7 @@ class ServiceListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.ServiceFilterSet
|
filterset = filters.ServiceFilterSet
|
||||||
filterset_form = forms.ServiceFilterForm
|
filterset_form = forms.ServiceFilterForm
|
||||||
table = tables.ServiceTable
|
table = tables.ServiceTable
|
||||||
template_name = 'ipam/service_list.html'
|
action_buttons = ('export',)
|
||||||
|
|
||||||
|
|
||||||
class ServiceView(PermissionRequiredMixin, View):
|
class ServiceView(PermissionRequiredMixin, View):
|
||||||
|
@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||||||
# Environment setup
|
# Environment setup
|
||||||
#
|
#
|
||||||
|
|
||||||
VERSION = '2.7.6-dev'
|
VERSION = '2.7.7-dev'
|
||||||
|
|
||||||
# Hostname
|
# Hostname
|
||||||
HOSTNAME = platform.node()
|
HOSTNAME = platform.node()
|
||||||
|
@ -190,15 +190,18 @@ $(document).ready(function() {
|
|||||||
$.each(element.attributes, function(index, attr){
|
$.each(element.attributes, function(index, attr){
|
||||||
if (attr.name.includes("data-additional-query-param-")){
|
if (attr.name.includes("data-additional-query-param-")){
|
||||||
var param_name = attr.name.split("data-additional-query-param-")[1];
|
var param_name = attr.name.split("data-additional-query-param-")[1];
|
||||||
if (param_name in parameters) {
|
|
||||||
if (Array.isArray(parameters[param_name])) {
|
$.each($.parseJSON(attr.value), function(index, value) {
|
||||||
parameters[param_name].push(attr.value)
|
if (param_name in parameters) {
|
||||||
|
if (Array.isArray(parameters[param_name])) {
|
||||||
|
parameters[param_name].push(value);
|
||||||
|
} else {
|
||||||
|
parameters[param_name] = [parameters[param_name], value];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
parameters[param_name] = [parameters[param_name], attr.value]
|
parameters[param_name] = value;
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
parameters[param_name] = attr.value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ class SecretRoleListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'secrets.view_secretrole'
|
permission_required = 'secrets.view_secretrole'
|
||||||
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
|
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
|
||||||
table = tables.SecretRoleTable
|
table = tables.SecretRoleTable
|
||||||
template_name = 'secrets/secretrole_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class SecretRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
class SecretRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -73,7 +72,7 @@ class SecretListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.SecretFilterSet
|
filterset = filters.SecretFilterSet
|
||||||
filterset_form = forms.SecretFilterForm
|
filterset_form = forms.SecretFilterForm
|
||||||
table = tables.SecretTable
|
table = tables.SecretTable
|
||||||
template_name = 'secrets/secret_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class SecretView(PermissionRequiredMixin, View):
|
class SecretView(PermissionRequiredMixin, View):
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.circuits.add_circuit %}
|
|
||||||
{% add_button 'circuits:circuit_add' %}
|
|
||||||
{% import_button 'circuits:circuit_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Circuits{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='circuits:circuit_bulk_edit' bulk_delete_url='circuits:circuit_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.circuits.add_circuittype %}
|
|
||||||
{% add_button 'circuits:circuittype_add' %}
|
|
||||||
{% import_button 'circuits:circuittype_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Circuit Types{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='circuits:circuittype_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.circuits.add_provider %}
|
|
||||||
{% add_button 'circuits:provider_add' %}
|
|
||||||
{% import_button 'circuits:provider_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Providers{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='circuits:provider_bulk_edit' bulk_delete_url='circuits:provider_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,20 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_cable %}
|
|
||||||
{% import_button 'dcim:cable_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Cables{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:cable_bulk_edit' bulk_delete_url='dcim:cable_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Console Ports{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:consoleport_bulk_edit' bulk_delete_url='dcim:consoleport_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Console Server Ports{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:consoleserverport_bulk_edit' bulk_delete_url='dcim:consoleserverport_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +1,24 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends 'utilities/obj_list.html' %}
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block bulk_buttons %}
|
||||||
<div class="pull-right noprint">
|
{% if perms.dcim.change_device %}
|
||||||
{% if perms.dcim.add_device %}
|
<div class="btn-group">
|
||||||
{% add_button 'dcim:device_add' %}
|
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
{% import_button 'dcim:device_import' %}
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% if perms.dcim.add_consoleport %}<li><a href="{% url 'dcim:device_bulk_add_consoleport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Console Ports</a></li>{% endif %}
|
||||||
|
{% if perms.dcim.add_consoleserverport %}<li><a href="{% url 'dcim:device_bulk_add_consoleserverport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Console Server Ports</a></li>{% endif %}
|
||||||
|
{% if perms.dcim.add_powerport %}<li><a href="{% url 'dcim:device_bulk_add_powerport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Power Ports</a></li>{% endif %}
|
||||||
|
{% if perms.dcim.add_poweroutlet %}<li><a href="{% url 'dcim:device_bulk_add_poweroutlet' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Power Outlets</a></li>{% endif %}
|
||||||
|
{% if perms.dcim.add_interface %}<li><a href="{% url 'dcim:device_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Interfaces</a></li>{% endif %}
|
||||||
|
{% if perms.dcim.add_devicebay %}<li><a href="{% url 'dcim:device_bulk_add_devicebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Device Bays</a></li>{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_virtualchassis %}
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:virtualchassis_add' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-primary btn-sm">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create Virtual Chassis
|
||||||
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Devices{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'dcim/inc/device_table.html' with bulk_edit_url='dcim:device_bulk_edit' bulk_delete_url='dcim:device_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Device Bays{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:devicebay_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_devicerole %}
|
|
||||||
{% add_button 'dcim:devicerole_add' %}
|
|
||||||
{% import_button 'dcim:devicerole_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Device Roles{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:devicerole_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_devicetype %}
|
|
||||||
{% add_button 'dcim:devicetype_add' %}
|
|
||||||
{% import_button 'dcim:devicetype_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Device Types{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:devicetype_bulk_edit' bulk_delete_url='dcim:devicetype_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Front Ports{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:frontport_bulk_edit' bulk_delete_url='dcim:frontport_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,24 +0,0 @@
|
|||||||
{% extends 'utilities/obj_table.html' %}
|
|
||||||
|
|
||||||
{% block extra_actions %}
|
|
||||||
{% if perms.dcim.change_device %}
|
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% if perms.dcim.add_consoleport %}<li><a href="{% url 'dcim:device_bulk_add_consoleport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Console Ports</a></li>{% endif %}
|
|
||||||
{% if perms.dcim.add_consoleserverport %}<li><a href="{% url 'dcim:device_bulk_add_consoleserverport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Console Server Ports</a></li>{% endif %}
|
|
||||||
{% if perms.dcim.add_powerport %}<li><a href="{% url 'dcim:device_bulk_add_powerport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Power Ports</a></li>{% endif %}
|
|
||||||
{% if perms.dcim.add_poweroutlet %}<li><a href="{% url 'dcim:device_bulk_add_poweroutlet' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Power Outlets</a></li>{% endif %}
|
|
||||||
{% if perms.dcim.add_interface %}<li><a href="{% url 'dcim:device_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Interfaces</a></li>{% endif %}
|
|
||||||
{% if perms.dcim.add_devicebay %}<li><a href="{% url 'dcim:device_bulk_add_devicebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Device Bays</a></li>{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.dcim.add_virtualchassis %}
|
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:virtualchassis_add' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-primary btn-sm">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create Virtual Chassis
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Interfaces{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:interface_bulk_edit' bulk_delete_url='dcim:interface_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
{% load helpers %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_devicetype %}
|
|
||||||
{% import_button 'dcim:inventoryitem_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Inventory Items{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:inventoryitem_bulk_edit' bulk_delete_url='dcim:inventoryitem_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_manufacturer %}
|
|
||||||
{% add_button 'dcim:manufacturer_add' %}
|
|
||||||
{% import_button 'dcim:manufacturer_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Manufacturers{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:manufacturer_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_platform %}
|
|
||||||
{% add_button 'dcim:platform_add' %}
|
|
||||||
{% import_button 'dcim:platform_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Platforms{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:platform_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_powerfeed %}
|
|
||||||
{% add_button 'dcim:powerfeed_add' %}
|
|
||||||
{% import_button 'dcim:powerfeed_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Power Feeds{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:powerfeed_bulk_edit' bulk_delete_url='dcim:powerfeed_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Power Outlets{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:poweroutlet_bulk_edit' bulk_delete_url='dcim:poweroutlet_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_powerpanel %}
|
|
||||||
{% add_button 'dcim:powerpanel_add' %}
|
|
||||||
{% import_button 'dcim:powerpanel_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Power Panels{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:powerpanel_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Power Ports{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:powerport_bulk_edit' bulk_delete_url='dcim:powerport_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_rack %}
|
|
||||||
{% add_button 'dcim:rack_add' %}
|
|
||||||
{% import_button 'dcim:rack_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Racks{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rack_bulk_edit' bulk_delete_url='dcim:rack_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_rackgroup %}
|
|
||||||
{% add_button 'dcim:rackgroup_add' %}
|
|
||||||
{% import_button 'dcim:rackgroup_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Rack Groups{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackgroup_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,14 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load helpers %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% block title %}Rack Reservations{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rackreservation_bulk_edit' bulk_delete_url='dcim:rackreservation_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_rackrole %}
|
|
||||||
{% add_button 'dcim:rackrole_add' %}
|
|
||||||
{% import_button 'dcim:rackrole_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Rack Roles{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:rackrole_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Rear Ports{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rearport_bulk_edit' bulk_delete_url='dcim:rearport_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_region %}
|
|
||||||
{% add_button 'dcim:region_add' %}
|
|
||||||
{% import_button 'dcim:region_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Regions{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:region_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.dcim.add_site %}
|
|
||||||
{% add_button 'dcim:site_add' %}
|
|
||||||
{% import_button 'dcim:site_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<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' bulk_delete_url='dcim:site_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
{% load helpers %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Virtual Chassis{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,19 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.extras.add_configcontext %}
|
|
||||||
{% add_button 'extras:configcontext_add' %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Config Contexts{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='extras:configcontext_bulk_edit' bulk_delete_url='extras:configcontext_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,20 +1,9 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends 'utilities/obj_list.html' %}
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block title %}Change Log{% endblock %}
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
{% block sidebar %}
|
||||||
</div>
|
<div class="text-muted">
|
||||||
<h1>{% block title %}Changelog{% endblock %}</h1>
|
Change log retention: {% if settings.CHANGELOG_RETENTION %}{{ settings.CHANGELOG_RETENTION }} days{% else %}Indefinite{% endif %}
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' %}
|
|
||||||
<div class="text-muted text-right">
|
|
||||||
Changelog retention: {% if settings.CHANGELOG_RETENTION %}{{ settings.CHANGELOG_RETENTION }} days{% else %}Indefinite{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% block title %}Tags{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='extras:tag_bulk_edit' bulk_delete_url='extras:tag_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -473,7 +473,7 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li class="dropdown-header">Logging</li>
|
<li class="dropdown-header">Logging</li>
|
||||||
<li{% if not perms.extras.view_objectchange %} class="disabled"{% endif %}>
|
<li{% if not perms.extras.view_objectchange %} class="disabled"{% endif %}>
|
||||||
<a href="{% url 'extras:objectchange_list' %}">Changelog</a>
|
<a href="{% url 'extras:objectchange_list' %}">Change Log</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="dropdown-header">Miscellaneous</li>
|
<li class="dropdown-header">Miscellaneous</li>
|
||||||
|
@ -1,31 +1,14 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends 'utilities/obj_list.html' %}
|
||||||
{% load buttons %}
|
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
|
||||||
{% block content %}
|
{% block sidebar %}
|
||||||
<div class="pull-right noprint">
|
<div class="panel panel-default">
|
||||||
{% if perms.ipam.add_aggregate %}
|
<div class="panel-heading">
|
||||||
{% add_button 'ipam:aggregate_add' %}
|
<strong><i class="fa fa-bar-chart"></i> Statistics</strong>
|
||||||
{% import_button 'ipam:aggregate_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Aggregates{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:aggregate_bulk_edit' bulk_delete_url='ipam:aggregate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong><i class="fa fa-bar-chart"></i> Statistics</strong>
|
|
||||||
</div>
|
|
||||||
<ul class="list-group">
|
|
||||||
<li class="list-group-item">Total IPv4 IPs <span class="badge">{{ ipv4_total|intcomma }}</span></li>
|
|
||||||
<li class="list-group-item">Total IPv6 /64s <span class="badge">{{ ipv6_total|intcomma }}</span></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<ul class="list-group">
|
||||||
</div>
|
<li class="list-group-item">Total IPv4 IPs <span class="badge">{{ ipv4_total|intcomma }}</span></li>
|
||||||
|
<li class="list-group-item">Total IPv6 /64s <span class="badge">{{ ipv6_total|intcomma }}</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.ipam.add_ipaddress %}
|
|
||||||
{% add_button 'ipam:ipaddress_add' %}
|
|
||||||
{% import_button 'ipam:ipaddress_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}IP Addresses{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,26 +1,9 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends 'utilities/obj_list.html' %}
|
||||||
{% load buttons %}
|
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block content %}
|
{% block buttons %}
|
||||||
<div class="pull-right noprint">
|
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand=None page=1 %}" class="btn btn-default{% if not request.GET.expand %} active{% endif %}">Collapse</a>
|
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand=None page=1 %}" class="btn btn-default{% if not request.GET.expand %} active{% endif %}">Collapse</a>
|
||||||
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand='on' page=1 %}" class="btn btn-default{% if request.GET.expand %} active{% endif %}">Expand</a>
|
<a href="{% url 'ipam:prefix_list' %}{% querystring request expand='on' page=1 %}" class="btn btn-default{% if request.GET.expand %} active{% endif %}">Expand</a>
|
||||||
</div>
|
</div>
|
||||||
{% if perms.ipam.add_prefix %}
|
|
||||||
{% add_button 'ipam:prefix_add' %}
|
|
||||||
{% import_button 'ipam:prefix_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Prefixes{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends 'utilities/obj_list.html' %}
|
||||||
{% load buttons %}
|
|
||||||
{% load humanize %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block buttons %}
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if request.GET.family == '6' %}
|
{% if request.GET.family == '6' %}
|
||||||
<a href="{% url 'ipam:rir_list' %}" class="btn btn-default">
|
<a href="{% url 'ipam:rir_list' %}" class="btn btn-default">
|
||||||
<span class="fa fa-table" aria-hidden="true"></span>
|
<span class="fa fa-table" aria-hidden="true"></span>
|
||||||
@ -15,22 +12,12 @@
|
|||||||
IPv6 Stats
|
IPv6 Stats
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.add_rir %}
|
{% endblock %}
|
||||||
{% add_button 'ipam:rir_add' %}
|
|
||||||
{% import_button 'ipam:rir_import' %}
|
{% block sidebar %}
|
||||||
{% endif %}
|
{% if request.GET.family == '6' %}
|
||||||
{% export_button content_type %}
|
<div class="alert alert-info">
|
||||||
</div>
|
<i class="fa fa-info-circle"></i> Numbers shown indicate /64 prefixes.
|
||||||
<h1>{% block title %}RIRs{% endblock %}</h1>
|
</div>
|
||||||
<div class="row">
|
{% endif %}
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='ipam:rir_bulk_delete' %}
|
|
||||||
{% if request.GET.family == '6' %}
|
|
||||||
<div class="alert alert-info pull-right"><strong>Note:</strong> Numbers shown indicate /64 prefixes.</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.ipam.add_role %}
|
|
||||||
{% add_button 'ipam:role_add' %}
|
|
||||||
{% import_button 'ipam:role_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Prefix/VLAN Roles{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='ipam:role_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Services{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:service_bulk_edit' bulk_delete_url='ipam:service_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.ipam.add_vlan %}
|
|
||||||
{% add_button 'ipam:vlan_add' %}
|
|
||||||
{% import_button 'ipam:vlan_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}VLANs{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vlan_bulk_edit' bulk_delete_url='ipam:vlan_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.ipam.add_vlangroup %}
|
|
||||||
{% add_button 'ipam:vlangroup_add' %}
|
|
||||||
{% import_button 'ipam:vlangroup_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}VLAN Groups{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='ipam:vlangroup_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.ipam.add_vrf %}
|
|
||||||
{% add_button 'ipam:vrf_add' %}
|
|
||||||
{% import_button 'ipam:vrf_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}VRFs{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='ipam:vrf_bulk_edit' bulk_delete_url='ipam:vrf_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,20 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.secrets.add_secret %}
|
|
||||||
{% import_button 'secrets:secret_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Secrets{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='secrets:secret_bulk_edit' bulk_delete_url='secrets:secret_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.secrets.add_secretrole %}
|
|
||||||
{% add_button 'secrets:secretrole_add' %}
|
|
||||||
{% import_button 'secrets:secretrole_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Secret Roles{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='secrets:secretrole_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.tenancy.add_tenant %}
|
|
||||||
{% add_button 'tenancy:tenant_add' %}
|
|
||||||
{% import_button 'tenancy:tenant_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Tenants{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='tenancy:tenant_bulk_edit' bulk_delete_url='tenancy:tenant_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.tenancy.add_tenantgroup %}
|
|
||||||
{% add_button 'tenancy:tenantgroup_add' %}
|
|
||||||
{% import_button 'tenancy:tenantgroup_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Tenant Groups{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='tenancy:tenantgroup_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
79
netbox/templates/utilities/obj_list.html
Normal file
79
netbox/templates/utilities/obj_list.html
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
{% load buttons %}
|
||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="pull-right noprint">
|
||||||
|
{% block buttons %}{% endblock %}
|
||||||
|
{% if permissions.add and 'add' in action_buttons %}
|
||||||
|
{% add_button content_type.model_class|url_name:"add" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if permissions.add and 'import' in action_buttons %}
|
||||||
|
{% import_button content_type.model_class|url_name:"import" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if 'export' in action_buttons %}
|
||||||
|
{% export_button content_type %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<h1>{% block title %}{{ content_type.model_class|model_name_plural|bettertitle }}{% endblock %}</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
{% with bulk_edit_url=content_type.model_class|url_name:"bulk_edit" bulk_delete_url=content_type.model_class|url_name:"bulk_delete" %}
|
||||||
|
{% if permissions.change or permissions.delete %}
|
||||||
|
<form method="post" class="form form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
|
||||||
|
{% if table.paginator.num_pages > 1 %}
|
||||||
|
<div id="select_all_box" class="hidden panel panel-default noprint">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="checkbox-inline">
|
||||||
|
<label for="select_all">
|
||||||
|
<input type="checkbox" id="select_all" name="_all" />
|
||||||
|
Select <strong>all {{ table.rows|length }} {{ table.data.verbose_name_plural }}</strong> matching query
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
{% if bulk_edit_url and permissions.change %}
|
||||||
|
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if bulk_querystring %}?{{ bulk_querystring }}{% elif request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled="disabled">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit All
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if bulk_delete_url and permissions.delete %}
|
||||||
|
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if bulk_querystring %}?{{ bulk_querystring }}{% elif request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled="disabled">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete All
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% include table_template|default:'responsive_table.html' %}
|
||||||
|
<div class="pull-left noprint">
|
||||||
|
{% block bulk_buttons %}{% endblock %}
|
||||||
|
{% if bulk_edit_url and permissions.change %}
|
||||||
|
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}" class="btn btn-warning btn-sm">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Selected
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if bulk_delete_url and permissions.delete %}
|
||||||
|
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}" class="btn btn-danger btn-sm">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
{% include table_template|default:'responsive_table.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 noprint">
|
||||||
|
{% if filter_form %}
|
||||||
|
{% include 'inc/search_panel.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% block sidebar %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -1,21 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.virtualization.add_cluster %}
|
|
||||||
{% add_button 'virtualization:cluster_add' %}
|
|
||||||
{% import_button 'virtualization:cluster_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Clusters{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_edit_url='virtualization:cluster_bulk_edit' bulk_delete_url='virtualization:cluster_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.virtualization.add_clustergroup %}
|
|
||||||
{% add_button 'virtualization:clustergroup_add' %}
|
|
||||||
{% import_button 'virtualization:clustergroup_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Cluster Groups{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='virtualization:clustergroup_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% if perms.virtualization.add_clustertype %}
|
|
||||||
{% add_button 'virtualization:clustertype_add' %}
|
|
||||||
{% import_button 'virtualization:clustertype_import' %}
|
|
||||||
{% endif %}
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Cluster Types{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'utilities/obj_table.html' with bulk_delete_url='virtualization:clustertype_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,14 +0,0 @@
|
|||||||
{% extends 'utilities/obj_table.html' %}
|
|
||||||
|
|
||||||
{% block extra_actions %}
|
|
||||||
{% if perms.virtualization.change_virtualmachine %}
|
|
||||||
<div class="btn-group">
|
|
||||||
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% if perms.dcim.add_interface %}<li><a href="{% url 'virtualization:virtualmachine_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Interfaces</a></li>{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
@ -1,21 +1,14 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends 'utilities/obj_list.html' %}
|
||||||
{% load buttons %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block bulk_buttons %}
|
||||||
<div class="pull-right noprint">
|
{% if perms.virtualization.change_virtualmachine %}
|
||||||
{% if perms.virtualization.add_virtualmachine %}
|
<div class="btn-group">
|
||||||
{% add_button 'virtualization:virtualmachine_add' %}
|
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
{% import_button 'virtualization:virtualmachine_import' %}
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% if perms.dcim.add_interface %}<li><a href="{% url 'virtualization:virtualmachine_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Interfaces</a></li>{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}Virtual Machines{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'virtualization/inc/virtualmachine_table.html' with bulk_edit_url='virtualization:virtualmachine_bulk_edit' bulk_delete_url='virtualization:virtualmachine_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -22,7 +22,6 @@ class TenantGroupListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'tenancy.view_tenantgroup'
|
permission_required = 'tenancy.view_tenantgroup'
|
||||||
queryset = TenantGroup.objects.annotate(tenant_count=Count('tenants'))
|
queryset = TenantGroup.objects.annotate(tenant_count=Count('tenants'))
|
||||||
table = tables.TenantGroupTable
|
table = tables.TenantGroupTable
|
||||||
template_name = 'tenancy/tenantgroup_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class TenantGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
class TenantGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -60,7 +59,6 @@ class TenantListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
filterset = filters.TenantFilterSet
|
filterset = filters.TenantFilterSet
|
||||||
filterset_form = forms.TenantFilterForm
|
filterset_form = forms.TenantFilterForm
|
||||||
table = tables.TenantTable
|
table = tables.TenantTable
|
||||||
template_name = 'tenancy/tenant_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class TenantView(PermissionRequiredMixin, View):
|
class TenantView(PermissionRequiredMixin, View):
|
||||||
|
@ -69,3 +69,16 @@ FILTER_LOOKUP_HELP_TEXT_MAP = dict(
|
|||||||
gte='greater than or equal',
|
gte='greater than or equal',
|
||||||
n='negated'
|
n='negated'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Keys for PostgreSQL advisory locks. These are arbitrary bigints used by
|
||||||
|
# the advisory_lock contextmanager. When a lock is acquired,
|
||||||
|
# one of these keys will be used to identify said lock.
|
||||||
|
#
|
||||||
|
# When adding a new key, pick something arbitrary and unique so
|
||||||
|
# that it is easily searchable in query logs.
|
||||||
|
|
||||||
|
ADVISORY_LOCK_KEYS = {
|
||||||
|
'available-prefixes': 100100,
|
||||||
|
'available-ips': 100200,
|
||||||
|
}
|
||||||
|
@ -309,12 +309,17 @@ class APISelect(SelectWithDisabled):
|
|||||||
|
|
||||||
def add_additional_query_param(self, name, value):
|
def add_additional_query_param(self, name, value):
|
||||||
"""
|
"""
|
||||||
Add details for an additional query param in the form of a data-* attribute.
|
Add details for an additional query param in the form of a data-* JSON-encoded list attribute.
|
||||||
|
|
||||||
:param name: The name of the query param
|
:param name: The name of the query param
|
||||||
:param value: The value of the query param
|
:param value: The value of the query param
|
||||||
"""
|
"""
|
||||||
self.attrs['data-additional-query-param-{}'.format(name)] = value
|
key = 'data-additional-query-param-{}'.format(name)
|
||||||
|
|
||||||
|
values = json.loads(self.attrs.get(key, '[]'))
|
||||||
|
values.append(value)
|
||||||
|
|
||||||
|
self.attrs[key] = json.dumps(values)
|
||||||
|
|
||||||
def add_conditional_query_param(self, condition, value):
|
def add_conditional_query_param(self, condition, value):
|
||||||
"""
|
"""
|
||||||
|
@ -10,7 +10,7 @@ INTERFACE_NAME_REGEX = r'(^(?P<type>[^\d\.:]+)?)' \
|
|||||||
r'(.(?P<vc>\d+)$)?'
|
r'(.(?P<vc>\d+)$)?'
|
||||||
|
|
||||||
|
|
||||||
def naturalize(value, max_length=None, integer_places=8):
|
def naturalize(value, max_length, integer_places=8):
|
||||||
"""
|
"""
|
||||||
Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings
|
Take an alphanumeric string and prepend all integers to `integer_places` places to ensure the strings
|
||||||
are ordered naturally. For example:
|
are ordered naturally. For example:
|
||||||
@ -39,10 +39,10 @@ def naturalize(value, max_length=None, integer_places=8):
|
|||||||
output.append(segment)
|
output.append(segment)
|
||||||
ret = ''.join(output)
|
ret = ''.join(output)
|
||||||
|
|
||||||
return ret[:max_length] if max_length else ret
|
return ret[:max_length]
|
||||||
|
|
||||||
|
|
||||||
def naturalize_interface(value, max_length=None):
|
def naturalize_interface(value, max_length):
|
||||||
"""
|
"""
|
||||||
Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old
|
Similar in nature to naturalize(), but takes into account a particular naming format adapted from the old
|
||||||
InterfaceManager.
|
InterfaceManager.
|
||||||
@ -77,4 +77,4 @@ def naturalize_interface(value, max_length=None):
|
|||||||
output.append('000000')
|
output.append('000000')
|
||||||
|
|
||||||
ret = ''.join(output)
|
ret = ''.join(output)
|
||||||
return ret[:max_length] if max_length else ret
|
return ret[:max_length]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import yaml
|
|
||||||
|
|
||||||
|
import yaml
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.urls import NoReverseMatch, reverse
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
@ -11,7 +12,6 @@ from markdown import markdown
|
|||||||
from utilities.choices import unpack_grouped_choices
|
from utilities.choices import unpack_grouped_choices
|
||||||
from utilities.utils import foreground_color
|
from utilities.utils import foreground_color
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@ -101,6 +101,21 @@ def model_name_plural(obj):
|
|||||||
return obj._meta.verbose_name_plural
|
return obj._meta.verbose_name_plural
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def url_name(model, action):
|
||||||
|
"""
|
||||||
|
Return the URL name for the given model and action, or None if invalid.
|
||||||
|
"""
|
||||||
|
url_name = '{}:{}_{}'.format(model._meta.app_label, model._meta.model_name, action)
|
||||||
|
try:
|
||||||
|
# Validate and return the URL name. We don't return the actual URL yet because many of the templates
|
||||||
|
# are written to pass a name to {% url %}.
|
||||||
|
reverse(url_name)
|
||||||
|
return url_name
|
||||||
|
except NoReverseMatch:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@register.filter()
|
@register.filter()
|
||||||
def contains(value, arg):
|
def contains(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -21,7 +21,10 @@ class NaturalizationTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for origin, naturalized in data:
|
for origin, naturalized in data:
|
||||||
self.assertEqual(naturalize(origin), naturalized)
|
self.assertEqual(naturalize(origin, max_length=50), naturalized)
|
||||||
|
|
||||||
|
def test_naturalize_max_length(self):
|
||||||
|
self.assertEqual(naturalize('abc123def456', max_length=10), 'abc0000012')
|
||||||
|
|
||||||
def test_naturalize_interface(self):
|
def test_naturalize_interface(self):
|
||||||
|
|
||||||
@ -40,4 +43,7 @@ class NaturalizationTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for origin, naturalized in data:
|
for origin, naturalized in data:
|
||||||
self.assertEqual(naturalize_interface(origin), naturalized)
|
self.assertEqual(naturalize_interface(origin, max_length=50), naturalized)
|
||||||
|
|
||||||
|
def test_naturalize_interface_max_length(self):
|
||||||
|
self.assertEqual(naturalize_interface('Gi1/2/3', max_length=20), '0001000299999999Gi00')
|
||||||
|
@ -71,7 +71,8 @@ class ObjectListView(View):
|
|||||||
filterset = None
|
filterset = None
|
||||||
filterset_form = None
|
filterset_form = None
|
||||||
table = None
|
table = None
|
||||||
template_name = None
|
template_name = 'utilities/obj_list.html'
|
||||||
|
action_buttons = ('add', 'import', 'export')
|
||||||
|
|
||||||
def queryset_to_yaml(self):
|
def queryset_to_yaml(self):
|
||||||
"""
|
"""
|
||||||
@ -156,9 +157,11 @@ class ObjectListView(View):
|
|||||||
# Provide a hook to tweak the queryset based on the request immediately prior to rendering the object list
|
# Provide a hook to tweak the queryset based on the request immediately prior to rendering the object list
|
||||||
self.queryset = self.alter_queryset(request)
|
self.queryset = self.alter_queryset(request)
|
||||||
|
|
||||||
# Compile user model permissions for access from within the template
|
# Compile a dictionary indicating which permissions are available to the current user for this model
|
||||||
perm_base_name = '{}.{{}}_{}'.format(model._meta.app_label, model._meta.model_name)
|
permissions = {}
|
||||||
permissions = {p: request.user.has_perm(perm_base_name.format(p)) for p in ['add', 'change', 'delete']}
|
for action in ('add', 'change', 'delete', 'view'):
|
||||||
|
perm_name = '{}.{}_{}'.format(model._meta.app_label, action, model._meta.model_name)
|
||||||
|
permissions[action] = request.user.has_perm(perm_name)
|
||||||
|
|
||||||
# Construct the table based on the user's permissions
|
# Construct the table based on the user's permissions
|
||||||
table = self.table(self.queryset)
|
table = self.table(self.queryset)
|
||||||
@ -176,6 +179,7 @@ class ObjectListView(View):
|
|||||||
'content_type': content_type,
|
'content_type': content_type,
|
||||||
'table': table,
|
'table': table,
|
||||||
'permissions': permissions,
|
'permissions': permissions,
|
||||||
|
'action_buttons': self.action_buttons,
|
||||||
'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
|
'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
|
||||||
}
|
}
|
||||||
context.update(self.extra_context())
|
context.update(self.extra_context())
|
||||||
@ -630,7 +634,7 @@ class BulkEditView(GetReturnURLMixin, View):
|
|||||||
post_data['pk'] = [obj.pk for obj in self.filterset(request.GET, model.objects.only('pk')).qs]
|
post_data['pk'] = [obj.pk for obj in self.filterset(request.GET, model.objects.only('pk')).qs]
|
||||||
|
|
||||||
if '_apply' in request.POST:
|
if '_apply' in request.POST:
|
||||||
form = self.form(model, request.POST, initial=request.GET)
|
form = self.form(model, request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
||||||
custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else []
|
custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else []
|
||||||
@ -714,10 +718,6 @@ class BulkEditView(GetReturnURLMixin, View):
|
|||||||
else:
|
else:
|
||||||
# Pass the PK list as initial data to avoid binding the form
|
# Pass the PK list as initial data to avoid binding the form
|
||||||
initial_data = querydict_to_dict(post_data)
|
initial_data = querydict_to_dict(post_data)
|
||||||
|
|
||||||
# Append any normal initial data (passed as GET parameters)
|
|
||||||
initial_data.update(request.GET)
|
|
||||||
|
|
||||||
form = self.form(model, initial=initial_data)
|
form = self.form(model, initial=initial_data)
|
||||||
|
|
||||||
# Retrieve objects being edited
|
# Retrieve objects being edited
|
||||||
|
@ -351,6 +351,7 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
)
|
)
|
||||||
role = DynamicModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/dcim/device-roles/",
|
api_url="/api/dcim/device-roles/",
|
||||||
additional_query_params={
|
additional_query_params={
|
||||||
@ -658,7 +659,10 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm):
|
|||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
@ -667,7 +671,10 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
@ -695,35 +702,12 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site
|
# Add current site to VLANs query params
|
||||||
vlan_choices = []
|
|
||||||
global_vlans = VLAN.objects.filter(site=None, group=None)
|
|
||||||
vlan_choices.append(
|
|
||||||
('Global', [(vlan.pk, vlan) for vlan in global_vlans])
|
|
||||||
)
|
|
||||||
for group in VLANGroup.objects.filter(site=None):
|
|
||||||
global_group_vlans = VLAN.objects.filter(group=group)
|
|
||||||
vlan_choices.append(
|
|
||||||
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
|
|
||||||
)
|
|
||||||
|
|
||||||
site = getattr(self.instance.parent, 'site', None)
|
site = getattr(self.instance.parent, 'site', None)
|
||||||
if site is not None:
|
if site is not None:
|
||||||
|
# Add current site to VLANs query params
|
||||||
# Add non-grouped site VLANs
|
self.fields['untagged_vlan'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
site_vlans = VLAN.objects.filter(site=site, group=None)
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
vlan_choices.append((site.name, [(vlan.pk, vlan) for vlan in site_vlans]))
|
|
||||||
|
|
||||||
# Add grouped site VLANs
|
|
||||||
for group in VLANGroup.objects.filter(site=site):
|
|
||||||
site_group_vlans = VLAN.objects.filter(group=group)
|
|
||||||
vlan_choices.append((
|
|
||||||
'{} / {}'.format(group.site.name, group.name),
|
|
||||||
[(vlan.pk, vlan) for vlan in site_group_vlans]
|
|
||||||
))
|
|
||||||
|
|
||||||
self.fields['untagged_vlan'].choices = [(None, '---------')] + vlan_choices
|
|
||||||
self.fields['tagged_vlans'].choices = vlan_choices
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
@ -784,7 +768,10 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
|
|||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
@ -793,7 +780,10 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
@ -807,35 +797,11 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
|
|||||||
pk=self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
pk=self.initial.get('virtual_machine') or self.data.get('virtual_machine')
|
||||||
)
|
)
|
||||||
|
|
||||||
# Limit VLAN choices to those in: global vlans, global groups, the current site's group, the current site
|
|
||||||
vlan_choices = []
|
|
||||||
global_vlans = VLAN.objects.filter(site=None, group=None)
|
|
||||||
vlan_choices.append(
|
|
||||||
('Global', [(vlan.pk, vlan) for vlan in global_vlans])
|
|
||||||
)
|
|
||||||
for group in VLANGroup.objects.filter(site=None):
|
|
||||||
global_group_vlans = VLAN.objects.filter(group=group)
|
|
||||||
vlan_choices.append(
|
|
||||||
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
|
|
||||||
)
|
|
||||||
|
|
||||||
site = getattr(virtual_machine.cluster, 'site', None)
|
site = getattr(virtual_machine.cluster, 'site', None)
|
||||||
if site is not None:
|
if site is not None:
|
||||||
|
# Add current site to VLANs query params
|
||||||
# Add non-grouped site VLANs
|
self.fields['untagged_vlan'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
site_vlans = VLAN.objects.filter(site=site, group=None)
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
vlan_choices.append((site.name, [(vlan.pk, vlan) for vlan in site_vlans]))
|
|
||||||
|
|
||||||
# Add grouped site VLANs
|
|
||||||
for group in VLANGroup.objects.filter(site=site):
|
|
||||||
site_group_vlans = VLAN.objects.filter(group=group)
|
|
||||||
vlan_choices.append((
|
|
||||||
'{} / {}'.format(group.site.name, group.name),
|
|
||||||
[(vlan.pk, vlan) for vlan in site_group_vlans]
|
|
||||||
))
|
|
||||||
|
|
||||||
self.fields['untagged_vlan'].choices = [(None, '---------')] + vlan_choices
|
|
||||||
self.fields['tagged_vlans'].choices = vlan_choices
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||||
@ -872,7 +838,10 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tagged_vlans = DynamicModelMultipleChoiceField(
|
tagged_vlans = DynamicModelMultipleChoiceField(
|
||||||
@ -881,7 +850,10 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlans/",
|
api_url="/api/ipam/vlans/",
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
full=True
|
full=True,
|
||||||
|
additional_query_params={
|
||||||
|
'site_id': 'null',
|
||||||
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -897,35 +869,11 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
if 'virtual_machine' in self.initial:
|
if 'virtual_machine' in self.initial:
|
||||||
parent_obj = VirtualMachine.objects.filter(pk=self.initial['virtual_machine']).first()
|
parent_obj = VirtualMachine.objects.filter(pk=self.initial['virtual_machine']).first()
|
||||||
|
|
||||||
# Limit VLAN choices to global VLANs, VLANs in global groups, the current site's group, the current site
|
site = getattr(parent_obj.cluster, 'site', None)
|
||||||
vlan_choices = []
|
if site is not None:
|
||||||
global_vlans = VLAN.objects.filter(site=None, group=None)
|
# Add current site to VLANs query params
|
||||||
vlan_choices.append(
|
self.fields['untagged_vlan'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
('Global', [(vlan.pk, vlan) for vlan in global_vlans])
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
)
|
|
||||||
for group in VLANGroup.objects.filter(site=None):
|
|
||||||
global_group_vlans = VLAN.objects.filter(group=group)
|
|
||||||
vlan_choices.append(
|
|
||||||
(group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])
|
|
||||||
)
|
|
||||||
if parent_obj.cluster is not None:
|
|
||||||
site = getattr(parent_obj.cluster, 'site', None)
|
|
||||||
if site is not None:
|
|
||||||
|
|
||||||
# Add non-grouped site VLANs
|
|
||||||
site_vlans = VLAN.objects.filter(site=site, group=None)
|
|
||||||
vlan_choices.append((site.name, [(vlan.pk, vlan) for vlan in site_vlans]))
|
|
||||||
|
|
||||||
# Add grouped site VLANs
|
|
||||||
for group in VLANGroup.objects.filter(site=site):
|
|
||||||
site_group_vlans = VLAN.objects.filter(group=group)
|
|
||||||
vlan_choices.append((
|
|
||||||
'{} / {}'.format(group.site.name, group.name),
|
|
||||||
[(vlan.pk, vlan) for vlan in site_group_vlans]
|
|
||||||
))
|
|
||||||
|
|
||||||
self.fields['untagged_vlan'].choices = [(None, '---------')] + vlan_choices
|
|
||||||
self.fields['tagged_vlans'].choices = vlan_choices
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -26,7 +26,6 @@ class ClusterTypeListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'virtualization.view_clustertype'
|
permission_required = 'virtualization.view_clustertype'
|
||||||
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
|
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
|
||||||
table = tables.ClusterTypeTable
|
table = tables.ClusterTypeTable
|
||||||
template_name = 'virtualization/clustertype_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterTypeCreateView(PermissionRequiredMixin, ObjectEditView):
|
class ClusterTypeCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -62,7 +61,6 @@ class ClusterGroupListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
permission_required = 'virtualization.view_clustergroup'
|
permission_required = 'virtualization.view_clustergroup'
|
||||||
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
|
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
|
||||||
table = tables.ClusterGroupTable
|
table = tables.ClusterGroupTable
|
||||||
template_name = 'virtualization/clustergroup_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
class ClusterGroupCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||||
@ -100,7 +98,6 @@ class ClusterListView(PermissionRequiredMixin, ObjectListView):
|
|||||||
table = tables.ClusterTable
|
table = tables.ClusterTable
|
||||||
filterset = filters.ClusterFilterSet
|
filterset = filters.ClusterFilterSet
|
||||||
filterset_form = forms.ClusterFilterForm
|
filterset_form = forms.ClusterFilterForm
|
||||||
template_name = 'virtualization/cluster_list.html'
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterView(PermissionRequiredMixin, View):
|
class ClusterView(PermissionRequiredMixin, View):
|
||||||
|
@ -4,6 +4,7 @@ django-cors-headers==3.2.1
|
|||||||
django-debug-toolbar==2.1
|
django-debug-toolbar==2.1
|
||||||
django-filter==2.2.0
|
django-filter==2.2.0
|
||||||
django-mptt==0.9.1
|
django-mptt==0.9.1
|
||||||
|
django-pglocks==1.0.4
|
||||||
django-prometheus==1.1.0
|
django-prometheus==1.1.0
|
||||||
django-rq==2.2.0
|
django-rq==2.2.0
|
||||||
django-tables2==2.2.1
|
django-tables2==2.2.1
|
||||||
|
Reference in New Issue
Block a user