mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 00:28:16 -06:00
commit
b907a649e9
3
.github/stale.yml
vendored
3
.github/stale.yml
vendored
@ -1,5 +1,8 @@
|
|||||||
# Configuration for Stale (https://github.com/apps/stale)
|
# Configuration for Stale (https://github.com/apps/stale)
|
||||||
|
|
||||||
|
# Pull requests are exempt from being marked as stale
|
||||||
|
only: issues
|
||||||
|
|
||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 14
|
daysUntilStale: 14
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -177,10 +177,11 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a
|
|||||||
|
|
||||||
All variables support the following default options:
|
All variables support the following default options:
|
||||||
|
|
||||||
* `label` - The name of the form field
|
|
||||||
* `description` - A brief description of the field
|
|
||||||
* `default` - The field's default value
|
* `default` - The field's default value
|
||||||
|
* `description` - A brief description of the field
|
||||||
|
* `label` - The name of the form field
|
||||||
* `required` - Indicates whether the field is mandatory (default: true)
|
* `required` - Indicates whether the field is mandatory (default: true)
|
||||||
|
* `widget` - The class of form widget to use (see the [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/widgets/))
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
@ -109,6 +109,20 @@ In order to send email, NetBox needs an email server configured. The following i
|
|||||||
* TIMEOUT - Amount of time to wait for a connection (seconds)
|
* TIMEOUT - Amount of time to wait for a connection (seconds)
|
||||||
* FROM_EMAIL - Sender address for emails sent by NetBox
|
* FROM_EMAIL - Sender address for emails sent by NetBox
|
||||||
|
|
||||||
|
Email is sent from NetBox only for critical events. If you would like to test the email server configuration please use the django function [send_mail()](https://docs.djangoproject.com/en/stable/topics/email/#send-mail):
|
||||||
|
|
||||||
|
```
|
||||||
|
# python ./manage.py nbshell
|
||||||
|
>>> from django.core.mail import send_mail
|
||||||
|
>>> send_mail(
|
||||||
|
'Test Email Subject',
|
||||||
|
'Test Email Body',
|
||||||
|
'noreply-netbox@example.com',
|
||||||
|
['users@example.com'],
|
||||||
|
fail_silently=False
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## EXEMPT_VIEW_PERMISSIONS
|
## EXEMPT_VIEW_PERMISSIONS
|
||||||
|
@ -21,7 +21,7 @@ NetBox requires access to a PostgreSQL database service to store data. This serv
|
|||||||
* `PASSWORD` - PostgreSQL password
|
* `PASSWORD` - PostgreSQL password
|
||||||
* `HOST` - Name or IP address of the database server (use `localhost` if running locally)
|
* `HOST` - Name or IP address of the database server (use `localhost` if running locally)
|
||||||
* `PORT` - TCP port of the PostgreSQL service; leave blank for default port (5432)
|
* `PORT` - TCP port of the PostgreSQL service; leave blank for default port (5432)
|
||||||
* `CONN_MAX_AGE` - Number in seconds for Netbox to keep database connections open. 150-300 seconds is typically a good starting point ([more info](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections)).
|
* `CONN_MAX_AGE` - Lifetime of a [persistent database connection](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections), in seconds (150-300 is recommended)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -36,6 +36,9 @@ DATABASE = {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
NetBox supports all PostgreSQL database options supported by the underlying Django framework. For a complete list of available parameters, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/settings/#databases).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## REDIS
|
## REDIS
|
||||||
@ -77,14 +80,56 @@ 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.
|
||||||
|
|
||||||
|
### Using Redis Sentinel
|
||||||
|
|
||||||
|
If you are using [Redis Sentinel](https://redis.io/topics/sentinel) for high-availability purposes, there is minimal
|
||||||
|
configuration necessary to convert NetBox to recognize it. It requires the removal of the `HOST` and `PORT` keys from
|
||||||
|
above and the addition of two new keys.
|
||||||
|
|
||||||
|
* `SENTINELS`: List of tuples or tuple of tuples with each inner tuple containing the name or IP address
|
||||||
|
of the Redis server and port for each sentinel instance to connect to
|
||||||
|
* `SENTINEL_SERVICE`: Name of the master / service to connect to
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
REDIS = {
|
||||||
|
'webhooks': {
|
||||||
|
'SENTINELS': [('mysentinel.redis.example.com', 6379)],
|
||||||
|
'SENTINEL_SERVICE': 'netbox',
|
||||||
|
'PASSWORD': '',
|
||||||
|
'DATABASE': 0,
|
||||||
|
'DEFAULT_TIMEOUT': 300,
|
||||||
|
'SSL': False,
|
||||||
|
},
|
||||||
|
'caching': {
|
||||||
|
'SENTINELS': [
|
||||||
|
('mysentinel.redis.example.com', 6379),
|
||||||
|
('othersentinel.redis.example.com', 6379)
|
||||||
|
],
|
||||||
|
'SENTINEL_SERVICE': 'netbox',
|
||||||
|
'PASSWORD': '',
|
||||||
|
'DATABASE': 1,
|
||||||
|
'DEFAULT_TIMEOUT': 300,
|
||||||
|
'SSL': False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
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
|
||||||
|
`SENTINELS`/`SENTINEL_SERVICE`.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## SECRET_KEY
|
## SECRET_KEY
|
||||||
|
@ -32,7 +32,7 @@ pycodestyle --ignore=W504,E501 netbox/
|
|||||||
|
|
||||||
The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and attacks.
|
The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and attacks.
|
||||||
|
|
||||||
If there's a strong case for introducing a new depdency, it must meet the following criteria:
|
If there's a strong case for introducing a new dependency, it must meet the following criteria:
|
||||||
|
|
||||||
* Its complete source code must be published and freely accessible without registration.
|
* Its complete source code must be published and freely accessible without registration.
|
||||||
* Its license must be conducive to inclusion in an open source project.
|
* Its license must be conducive to inclusion in an open source project.
|
||||||
@ -45,10 +45,18 @@ When adding a new dependency, a short description of the package and the URL of
|
|||||||
|
|
||||||
* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and open a bug so that the entire code base can be evaluated at a later point.
|
* When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and open a bug so that the entire code base can be evaluated at a later point.
|
||||||
|
|
||||||
|
* Prioritize readability over concision. Python is a very flexible language that typically gives us several options for expressing a given piece of logic, but some may be more friendly to the reader than others. (List comprehensions are particularly vulnerable to over-optimization.) Always remain considerate of the future reader who may need to interpret your code without the benefit of the context within which you are writing it.
|
||||||
|
|
||||||
* No easter eggs. While they can be fun, NetBox must be considered as a business-critical tool. The potential, however minor, for introducing a bug caused by unnecessary logic is best avoided entirely.
|
* No easter eggs. While they can be fun, NetBox must be considered as a business-critical tool. The potential, however minor, for introducing a bug caused by unnecessary logic is best avoided entirely.
|
||||||
|
|
||||||
* Constants (variables which generally do not change) should be declared in `constants.py` within each app. Wildcard imports from the file are acceptable.
|
* Constants (variables which generally do not change) should be declared in `constants.py` within each app. Wildcard imports from the file are acceptable.
|
||||||
|
|
||||||
* Every model should have a docstring. Every custom method should include an expalantion of its function.
|
* Every model should have a docstring. Every custom method should include an explanation of its function.
|
||||||
|
|
||||||
* Nested API serializers generate minimal representations of an object. These are stored separately from the primary serializers to avoid circular dependencies. Always import nested serializers from other apps directly. For example, from within the DCIM app you would write `from ipam.api.nested_serializers import NestedIPAddressSerializer`.
|
* Nested API serializers generate minimal representations of an object. These are stored separately from the primary serializers to avoid circular dependencies. Always import nested serializers from other apps directly. For example, from within the DCIM app you would write `from ipam.api.nested_serializers import NestedIPAddressSerializer`.
|
||||||
|
|
||||||
|
## Branding
|
||||||
|
|
||||||
|
* When referring to NetBox in writing, use the proper form "NetBox," with the letters N and B capitalized. The lowercase form "netbox" should be used in code, filenames, etc. But never "Netbox" or any other deviation.
|
||||||
|
|
||||||
|
* There is an SVG form of the NetBox logo at [docs/netbox_logo.svg](../netbox_logo.svg). It is preferred to use this logo for all purposes as it scales to arbitrary sizes without loss of resolution. If a raster image is required, the SVG logo should be converted to a PNG image of the prescribed size.
|
||||||
|
@ -4,7 +4,7 @@ The following sections detail how to set up a new instance of NetBox:
|
|||||||
|
|
||||||
1. [PostgreSQL database](1-postgresql.md)
|
1. [PostgreSQL database](1-postgresql.md)
|
||||||
2. [NetBox components](2-netbox.md)
|
2. [NetBox components](2-netbox.md)
|
||||||
3. [HTTP dameon](3-http-daemon.md)
|
3. [HTTP daemon](3-http-daemon.md)
|
||||||
4. [LDAP authentication](4-ldap.md) (optional)
|
4. [LDAP authentication](4-ldap.md) (optional)
|
||||||
|
|
||||||
# Upgrading
|
# Upgrading
|
||||||
|
@ -88,7 +88,7 @@ Finally, restart the WSGI services to run the new code. If you followed this gui
|
|||||||
|
|
||||||
```no-highlight
|
```no-highlight
|
||||||
# sudo systemctl restart netbox
|
# sudo systemctl restart netbox
|
||||||
# sudo systemctl restart netbox-rqworker
|
# sudo systemctl restart netbox-rq
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
@ -1,8 +1,60 @@
|
|||||||
# v2.7.5 (FUTURE)
|
# 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
|
## 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)
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
* [#3766](https://github.com/netbox-community/netbox/issues/3766) - Allow custom script authors to specify the form widget for each variable
|
||||||
|
* [#3799](https://github.com/netbox-community/netbox/issues/3799) - Greatly improve performance when ordering device components
|
||||||
|
* [#3984](https://github.com/netbox-community/netbox/issues/3984) - Add support for Redis Sentinel
|
||||||
|
* [#3986](https://github.com/netbox-community/netbox/issues/3986) - Include position numbers in SVG image when rendering rack elevations
|
||||||
|
* [#4093](https://github.com/netbox-community/netbox/issues/4093) - Add more status choices for virtual machines
|
||||||
|
* [#4100](https://github.com/netbox-community/netbox/issues/4100) - Add device filter to component list views
|
||||||
|
* [#4113](https://github.com/netbox-community/netbox/issues/4113) - Add bulk edit functionality for device type components
|
||||||
|
* [#4116](https://github.com/netbox-community/netbox/issues/4116) - Enable bulk edit and delete functions for device component list views
|
||||||
|
* [#4129](https://github.com/netbox-community/netbox/issues/4129) - Add buttons to delete individual device type components
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* [#3507](https://github.com/netbox-community/netbox/issues/3507) - Fix filtering IP addresses by multiple devices
|
||||||
|
* [#3995](https://github.com/netbox-community/netbox/issues/3995) - Make dropdown menus in the navigation bar scrollable on small screens
|
||||||
|
* [#4083](https://github.com/netbox-community/netbox/issues/4083) - Permit nullifying applicable choice fields via API requests
|
||||||
* [#4089](https://github.com/netbox-community/netbox/issues/4089) - Selection of power outlet type during bulk update is optional
|
* [#4089](https://github.com/netbox-community/netbox/issues/4089) - Selection of power outlet type during bulk update is optional
|
||||||
|
* [#4090](https://github.com/netbox-community/netbox/issues/4090) - Render URL custom fields as links under object view
|
||||||
|
* [#4091](https://github.com/netbox-community/netbox/issues/4091) - Fix filtering of objects by custom fields using UI search form
|
||||||
|
* [#4099](https://github.com/netbox-community/netbox/issues/4099) - Linkify interfaces on global interfaces list
|
||||||
|
* [#4108](https://github.com/netbox-community/netbox/issues/4108) - Avoid extraneous database queries when rendering search forms
|
||||||
|
* [#4134](https://github.com/netbox-community/netbox/issues/4134) - Device power ports and outlets should inherit type from the parent device type
|
||||||
|
* [#4137](https://github.com/netbox-community/netbox/issues/4137) - Disable occupied terminations when connecting a cable to a circuit
|
||||||
|
* [#4138](https://github.com/netbox-community/netbox/issues/4138) - Restore device bay counts in rack elevation diagrams
|
||||||
|
* [#4146](https://github.com/netbox-community/netbox/issues/4146) - Fix enforcement of secret role assignment for secret decryption
|
||||||
|
* [#4150](https://github.com/netbox-community/netbox/issues/4150) - Correct YAML rendering of config contexts
|
||||||
|
* [#4159](https://github.com/netbox-community/netbox/issues/4159) - Fix implementation of Redis caching configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# v2.7.4 (2020-02-04)
|
# v2.7.4 (2020-02-04)
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ pages:
|
|||||||
- Prometheus Metrics: 'additional-features/prometheus-metrics.md'
|
- Prometheus Metrics: 'additional-features/prometheus-metrics.md'
|
||||||
- Reports: 'additional-features/reports.md'
|
- Reports: 'additional-features/reports.md'
|
||||||
- Tags: 'additional-features/tags.md'
|
- Tags: 'additional-features/tags.md'
|
||||||
- Topology Maps: 'additional-features/topology-maps.md'
|
|
||||||
- Webhooks: 'additional-features/webhooks.md'
|
- Webhooks: 'additional-features/webhooks.md'
|
||||||
- Administration:
|
- Administration:
|
||||||
- Replicating NetBox: 'administration/replicating-netbox.md'
|
- Replicating NetBox: 'administration/replicating-netbox.md'
|
||||||
|
@ -15,15 +15,15 @@ router = routers.DefaultRouter()
|
|||||||
router.APIRootView = CircuitsRootView
|
router.APIRootView = CircuitsRootView
|
||||||
|
|
||||||
# Field choices
|
# Field choices
|
||||||
router.register(r'_choices', views.CircuitsFieldChoicesViewSet, basename='field-choice')
|
router.register('_choices', views.CircuitsFieldChoicesViewSet, basename='field-choice')
|
||||||
|
|
||||||
# Providers
|
# Providers
|
||||||
router.register(r'providers', views.ProviderViewSet)
|
router.register('providers', views.ProviderViewSet)
|
||||||
|
|
||||||
# Circuits
|
# Circuits
|
||||||
router.register(r'circuit-types', views.CircuitTypeViewSet)
|
router.register('circuit-types', views.CircuitTypeViewSet)
|
||||||
router.register(r'circuits', views.CircuitViewSet)
|
router.register('circuits', views.CircuitViewSet)
|
||||||
router.register(r'circuit-terminations', views.CircuitTerminationViewSet)
|
router.register('circuit-terminations', views.CircuitTerminationViewSet)
|
||||||
|
|
||||||
app_name = 'circuits-api'
|
app_name = 'circuits-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
@ -9,7 +9,8 @@ from tenancy.forms import TenancyFilterForm, TenancyForm
|
|||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField, DatePicker,
|
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField, DatePicker,
|
||||||
FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField
|
DynamicModelChoiceField, DynamicModelMultipleChoiceField, SmallTextarea, SlugField, StaticSelect2,
|
||||||
|
StaticSelect2Multiple, TagFilterField,
|
||||||
)
|
)
|
||||||
from .choices import CircuitStatusChoices
|
from .choices import CircuitStatusChoices
|
||||||
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
from .models import Circuit, CircuitTermination, CircuitType, Provider
|
||||||
@ -107,7 +108,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
region = FilterChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
@ -119,9 +120,10 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
@ -164,6 +166,18 @@ class CircuitTypeCSVForm(forms.ModelForm):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
|
provider = DynamicModelChoiceField(
|
||||||
|
queryset=Provider.objects.all(),
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/circuits/providers/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
type = DynamicModelChoiceField(
|
||||||
|
queryset=CircuitType.objects.all(),
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/circuits/circuit-types/"
|
||||||
|
)
|
||||||
|
)
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
required=False
|
required=False
|
||||||
@ -180,12 +194,6 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
'commit_rate': "Committed rate",
|
'commit_rate': "Committed rate",
|
||||||
}
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
'provider': APISelect(
|
|
||||||
api_url="/api/circuits/providers/"
|
|
||||||
),
|
|
||||||
'type': APISelect(
|
|
||||||
api_url="/api/circuits/circuit-types/"
|
|
||||||
),
|
|
||||||
'status': StaticSelect2(),
|
'status': StaticSelect2(),
|
||||||
'install_date': DatePicker(),
|
'install_date': DatePicker(),
|
||||||
}
|
}
|
||||||
@ -235,14 +243,14 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
|||||||
queryset=Circuit.objects.all(),
|
queryset=Circuit.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
)
|
)
|
||||||
type = forms.ModelChoiceField(
|
type = DynamicModelChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/circuits/circuit-types/"
|
api_url="/api/circuits/circuit-types/"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
provider = forms.ModelChoiceField(
|
provider = DynamicModelChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -255,7 +263,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
|||||||
initial='',
|
initial='',
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
tenant = forms.ModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -290,17 +298,19 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
|||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
type = FilterChoiceField(
|
type = DynamicModelMultipleChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/circuits/circuit-types/",
|
api_url="/api/circuits/circuit-types/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
provider = FilterChoiceField(
|
provider = DynamicModelMultipleChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/circuits/providers/",
|
api_url="/api/circuits/providers/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
@ -311,7 +321,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
|||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2Multiple()
|
widget=StaticSelect2Multiple()
|
||||||
)
|
)
|
||||||
region = forms.ModelMultipleChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
@ -323,9 +333,10 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
|
@ -2,10 +2,10 @@ import datetime
|
|||||||
|
|
||||||
from circuits.choices import *
|
from circuits.choices import *
|
||||||
from circuits.models import Circuit, CircuitType, Provider
|
from circuits.models import Circuit, CircuitType, Provider
|
||||||
from utilities.testing import StandardTestCases
|
from utilities.testing import ViewTestCases
|
||||||
|
|
||||||
|
|
||||||
class ProviderTestCase(StandardTestCases.Views):
|
class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Provider
|
model = Provider
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -46,14 +46,9 @@ class ProviderTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeTestCase(StandardTestCases.Views):
|
class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -77,7 +72,7 @@ class CircuitTypeTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CircuitTestCase(StandardTestCases.Views):
|
class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Circuit
|
model = Circuit
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -9,42 +9,42 @@ app_name = 'circuits'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
# Providers
|
# Providers
|
||||||
path(r'providers/', views.ProviderListView.as_view(), name='provider_list'),
|
path('providers/', views.ProviderListView.as_view(), name='provider_list'),
|
||||||
path(r'providers/add/', views.ProviderCreateView.as_view(), name='provider_add'),
|
path('providers/add/', views.ProviderCreateView.as_view(), name='provider_add'),
|
||||||
path(r'providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'),
|
path('providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'),
|
||||||
path(r'providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
|
path('providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
|
||||||
path(r'providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
|
path('providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
|
||||||
path(r'providers/<slug:slug>/', views.ProviderView.as_view(), name='provider'),
|
path('providers/<slug:slug>/', views.ProviderView.as_view(), name='provider'),
|
||||||
path(r'providers/<slug:slug>/edit/', views.ProviderEditView.as_view(), name='provider_edit'),
|
path('providers/<slug:slug>/edit/', views.ProviderEditView.as_view(), name='provider_edit'),
|
||||||
path(r'providers/<slug:slug>/delete/', views.ProviderDeleteView.as_view(), name='provider_delete'),
|
path('providers/<slug:slug>/delete/', views.ProviderDeleteView.as_view(), name='provider_delete'),
|
||||||
path(r'providers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
path('providers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
|
||||||
|
|
||||||
# Circuit types
|
# Circuit types
|
||||||
path(r'circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||||
path(r'circuit-types/add/', views.CircuitTypeCreateView.as_view(), name='circuittype_add'),
|
path('circuit-types/add/', views.CircuitTypeCreateView.as_view(), name='circuittype_add'),
|
||||||
path(r'circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
path('circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
||||||
path(r'circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
path('circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
||||||
path(r'circuit-types/<slug:slug>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
path('circuit-types/<slug:slug>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
||||||
path(r'circuit-types/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
|
path('circuit-types/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
|
||||||
|
|
||||||
# Circuits
|
# Circuits
|
||||||
path(r'circuits/', views.CircuitListView.as_view(), name='circuit_list'),
|
path('circuits/', views.CircuitListView.as_view(), name='circuit_list'),
|
||||||
path(r'circuits/add/', views.CircuitCreateView.as_view(), name='circuit_add'),
|
path('circuits/add/', views.CircuitCreateView.as_view(), name='circuit_add'),
|
||||||
path(r'circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'),
|
path('circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'),
|
||||||
path(r'circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
|
path('circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
|
||||||
path(r'circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
|
path('circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
|
||||||
path(r'circuits/<int:pk>/', views.CircuitView.as_view(), name='circuit'),
|
path('circuits/<int:pk>/', views.CircuitView.as_view(), name='circuit'),
|
||||||
path(r'circuits/<int:pk>/edit/', views.CircuitEditView.as_view(), name='circuit_edit'),
|
path('circuits/<int:pk>/edit/', views.CircuitEditView.as_view(), name='circuit_edit'),
|
||||||
path(r'circuits/<int:pk>/delete/', views.CircuitDeleteView.as_view(), name='circuit_delete'),
|
path('circuits/<int:pk>/delete/', views.CircuitDeleteView.as_view(), name='circuit_delete'),
|
||||||
path(r'circuits/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
|
path('circuits/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
|
||||||
path(r'circuits/<int:pk>/terminations/swap/', views.circuit_terminations_swap, name='circuit_terminations_swap'),
|
path('circuits/<int:pk>/terminations/swap/', views.circuit_terminations_swap, name='circuit_terminations_swap'),
|
||||||
|
|
||||||
# Circuit terminations
|
# Circuit terminations
|
||||||
|
|
||||||
path(r'circuits/<int:circuit>/terminations/add/', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
|
path('circuits/<int:circuit>/terminations/add/', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
|
||||||
path(r'circuit-terminations/<int:pk>/edit/', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
|
path('circuit-terminations/<int:pk>/edit/', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
|
||||||
path(r'circuit-terminations/<int:pk>/delete/', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
|
path('circuit-terminations/<int:pk>/delete/', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
|
||||||
path(r'circuit-terminations/<int:termination_a_id>/connect/<str:termination_b_type>/', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
|
path('circuit-terminations/<int:termination_a_id>/connect/<str:termination_b_type>/', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
|
||||||
path(r'circuit-terminations/<int:pk>/trace/', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
|
path('circuit-terminations/<int:pk>/trace/', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -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):
|
||||||
|
@ -117,9 +117,9 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
|||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
status = ChoiceField(choices=RackStatusChoices, required=False)
|
status = ChoiceField(choices=RackStatusChoices, required=False)
|
||||||
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
||||||
type = ChoiceField(choices=RackTypeChoices, required=False, allow_null=True)
|
type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False)
|
||||||
width = ChoiceField(choices=RackWidthChoices, required=False)
|
width = ChoiceField(choices=RackWidthChoices, required=False)
|
||||||
outer_unit = ChoiceField(choices=RackDimensionUnitChoices, required=False)
|
outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False)
|
||||||
tags = TagListSerializerField(required=False)
|
tags = TagListSerializerField(required=False)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = serializers.IntegerField(read_only=True)
|
||||||
powerfeed_count = serializers.IntegerField(read_only=True)
|
powerfeed_count = serializers.IntegerField(read_only=True)
|
||||||
@ -212,7 +212,7 @@ class ManufacturerSerializer(ValidatedModelSerializer):
|
|||||||
|
|
||||||
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||||
manufacturer = NestedManufacturerSerializer()
|
manufacturer = NestedManufacturerSerializer()
|
||||||
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, required=False, allow_null=True)
|
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
|
||||||
tags = TagListSerializerField(required=False)
|
tags = TagListSerializerField(required=False)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
@ -228,6 +228,7 @@ class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -240,6 +241,7 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -252,6 +254,7 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -264,6 +267,7 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
|||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
power_port = PowerPortTemplateSerializer(
|
power_port = PowerPortTemplateSerializer(
|
||||||
@ -271,8 +275,8 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
|||||||
)
|
)
|
||||||
feed_leg = ChoiceField(
|
feed_leg = ChoiceField(
|
||||||
choices=PowerOutletFeedLegChoices,
|
choices=PowerOutletFeedLegChoices,
|
||||||
required=False,
|
allow_blank=True,
|
||||||
allow_null=True
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -351,7 +355,7 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
|||||||
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
rack = NestedRackSerializer(required=False, allow_null=True)
|
rack = NestedRackSerializer(required=False, allow_null=True)
|
||||||
face = ChoiceField(choices=DeviceFaceChoices, required=False, allow_null=True)
|
face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, required=False)
|
||||||
status = ChoiceField(choices=DeviceStatusChoices, required=False)
|
status = ChoiceField(choices=DeviceStatusChoices, required=False)
|
||||||
primary_ip = NestedIPAddressSerializer(read_only=True)
|
primary_ip = NestedIPAddressSerializer(read_only=True)
|
||||||
primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
|
primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||||
@ -420,6 +424,7 @@ class ConsoleServerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer)
|
|||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
cable = NestedCableSerializer(read_only=True)
|
cable = NestedCableSerializer(read_only=True)
|
||||||
@ -437,6 +442,7 @@ class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
|||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
cable = NestedCableSerializer(read_only=True)
|
cable = NestedCableSerializer(read_only=True)
|
||||||
@ -454,6 +460,7 @@ class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
|||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
power_port = NestedPowerPortSerializer(
|
power_port = NestedPowerPortSerializer(
|
||||||
@ -461,8 +468,8 @@ class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
|||||||
)
|
)
|
||||||
feed_leg = ChoiceField(
|
feed_leg = ChoiceField(
|
||||||
choices=PowerOutletFeedLegChoices,
|
choices=PowerOutletFeedLegChoices,
|
||||||
required=False,
|
allow_blank=True,
|
||||||
allow_null=True
|
required=False
|
||||||
)
|
)
|
||||||
cable = NestedCableSerializer(
|
cable = NestedCableSerializer(
|
||||||
read_only=True
|
read_only=True
|
||||||
@ -483,6 +490,7 @@ class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
|||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
type = ChoiceField(
|
type = ChoiceField(
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
cable = NestedCableSerializer(read_only=True)
|
cable = NestedCableSerializer(read_only=True)
|
||||||
@ -500,7 +508,7 @@ class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
|
|||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
type = ChoiceField(choices=InterfaceTypeChoices, required=False)
|
type = ChoiceField(choices=InterfaceTypeChoices, required=False)
|
||||||
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||||
mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_null=True)
|
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
|
||||||
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
|
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||||
tagged_vlans = SerializedPKRelatedField(
|
tagged_vlans = SerializedPKRelatedField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
@ -617,7 +625,7 @@ class CableSerializer(ValidatedModelSerializer):
|
|||||||
termination_a = serializers.SerializerMethodField(read_only=True)
|
termination_a = serializers.SerializerMethodField(read_only=True)
|
||||||
termination_b = serializers.SerializerMethodField(read_only=True)
|
termination_b = serializers.SerializerMethodField(read_only=True)
|
||||||
status = ChoiceField(choices=CableStatusChoices, required=False)
|
status = ChoiceField(choices=CableStatusChoices, required=False)
|
||||||
length_unit = ChoiceField(choices=CableLengthUnitChoices, required=False, allow_null=True)
|
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
|
@ -15,65 +15,65 @@ router = routers.DefaultRouter()
|
|||||||
router.APIRootView = DCIMRootView
|
router.APIRootView = DCIMRootView
|
||||||
|
|
||||||
# Field choices
|
# Field choices
|
||||||
router.register(r'_choices', views.DCIMFieldChoicesViewSet, basename='field-choice')
|
router.register('_choices', views.DCIMFieldChoicesViewSet, basename='field-choice')
|
||||||
|
|
||||||
# Sites
|
# Sites
|
||||||
router.register(r'regions', views.RegionViewSet)
|
router.register('regions', views.RegionViewSet)
|
||||||
router.register(r'sites', views.SiteViewSet)
|
router.register('sites', views.SiteViewSet)
|
||||||
|
|
||||||
# Racks
|
# Racks
|
||||||
router.register(r'rack-groups', views.RackGroupViewSet)
|
router.register('rack-groups', views.RackGroupViewSet)
|
||||||
router.register(r'rack-roles', views.RackRoleViewSet)
|
router.register('rack-roles', views.RackRoleViewSet)
|
||||||
router.register(r'racks', views.RackViewSet)
|
router.register('racks', views.RackViewSet)
|
||||||
router.register(r'rack-reservations', views.RackReservationViewSet)
|
router.register('rack-reservations', views.RackReservationViewSet)
|
||||||
|
|
||||||
# Device types
|
# Device types
|
||||||
router.register(r'manufacturers', views.ManufacturerViewSet)
|
router.register('manufacturers', views.ManufacturerViewSet)
|
||||||
router.register(r'device-types', views.DeviceTypeViewSet)
|
router.register('device-types', views.DeviceTypeViewSet)
|
||||||
|
|
||||||
# Device type components
|
# Device type components
|
||||||
router.register(r'console-port-templates', views.ConsolePortTemplateViewSet)
|
router.register('console-port-templates', views.ConsolePortTemplateViewSet)
|
||||||
router.register(r'console-server-port-templates', views.ConsoleServerPortTemplateViewSet)
|
router.register('console-server-port-templates', views.ConsoleServerPortTemplateViewSet)
|
||||||
router.register(r'power-port-templates', views.PowerPortTemplateViewSet)
|
router.register('power-port-templates', views.PowerPortTemplateViewSet)
|
||||||
router.register(r'power-outlet-templates', views.PowerOutletTemplateViewSet)
|
router.register('power-outlet-templates', views.PowerOutletTemplateViewSet)
|
||||||
router.register(r'interface-templates', views.InterfaceTemplateViewSet)
|
router.register('interface-templates', views.InterfaceTemplateViewSet)
|
||||||
router.register(r'front-port-templates', views.FrontPortTemplateViewSet)
|
router.register('front-port-templates', views.FrontPortTemplateViewSet)
|
||||||
router.register(r'rear-port-templates', views.RearPortTemplateViewSet)
|
router.register('rear-port-templates', views.RearPortTemplateViewSet)
|
||||||
router.register(r'device-bay-templates', views.DeviceBayTemplateViewSet)
|
router.register('device-bay-templates', views.DeviceBayTemplateViewSet)
|
||||||
|
|
||||||
# Devices
|
# Devices
|
||||||
router.register(r'device-roles', views.DeviceRoleViewSet)
|
router.register('device-roles', views.DeviceRoleViewSet)
|
||||||
router.register(r'platforms', views.PlatformViewSet)
|
router.register('platforms', views.PlatformViewSet)
|
||||||
router.register(r'devices', views.DeviceViewSet)
|
router.register('devices', views.DeviceViewSet)
|
||||||
|
|
||||||
# Device components
|
# Device components
|
||||||
router.register(r'console-ports', views.ConsolePortViewSet)
|
router.register('console-ports', views.ConsolePortViewSet)
|
||||||
router.register(r'console-server-ports', views.ConsoleServerPortViewSet)
|
router.register('console-server-ports', views.ConsoleServerPortViewSet)
|
||||||
router.register(r'power-ports', views.PowerPortViewSet)
|
router.register('power-ports', views.PowerPortViewSet)
|
||||||
router.register(r'power-outlets', views.PowerOutletViewSet)
|
router.register('power-outlets', views.PowerOutletViewSet)
|
||||||
router.register(r'interfaces', views.InterfaceViewSet)
|
router.register('interfaces', views.InterfaceViewSet)
|
||||||
router.register(r'front-ports', views.FrontPortViewSet)
|
router.register('front-ports', views.FrontPortViewSet)
|
||||||
router.register(r'rear-ports', views.RearPortViewSet)
|
router.register('rear-ports', views.RearPortViewSet)
|
||||||
router.register(r'device-bays', views.DeviceBayViewSet)
|
router.register('device-bays', views.DeviceBayViewSet)
|
||||||
router.register(r'inventory-items', views.InventoryItemViewSet)
|
router.register('inventory-items', views.InventoryItemViewSet)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
router.register(r'console-connections', views.ConsoleConnectionViewSet, basename='consoleconnections')
|
router.register('console-connections', views.ConsoleConnectionViewSet, basename='consoleconnections')
|
||||||
router.register(r'power-connections', views.PowerConnectionViewSet, basename='powerconnections')
|
router.register('power-connections', views.PowerConnectionViewSet, basename='powerconnections')
|
||||||
router.register(r'interface-connections', views.InterfaceConnectionViewSet, basename='interfaceconnections')
|
router.register('interface-connections', views.InterfaceConnectionViewSet, basename='interfaceconnections')
|
||||||
|
|
||||||
# Cables
|
# Cables
|
||||||
router.register(r'cables', views.CableViewSet)
|
router.register('cables', views.CableViewSet)
|
||||||
|
|
||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
router.register(r'virtual-chassis', views.VirtualChassisViewSet)
|
router.register('virtual-chassis', views.VirtualChassisViewSet)
|
||||||
|
|
||||||
# Power
|
# Power
|
||||||
router.register(r'power-panels', views.PowerPanelViewSet)
|
router.register('power-panels', views.PowerPanelViewSet)
|
||||||
router.register(r'power-feeds', views.PowerFeedViewSet)
|
router.register('power-feeds', views.PowerFeedViewSet)
|
||||||
|
|
||||||
# Miscellaneous
|
# Miscellaneous
|
||||||
router.register(r'connected-device', views.ConnectedDeviceViewSet, basename='connected-device')
|
router.register('connected-device', views.ConnectedDeviceViewSet, basename='connected-device')
|
||||||
|
|
||||||
app_name = 'dcim-api'
|
app_name = 'dcim-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
@ -9,6 +9,8 @@ from .choices import InterfaceTypeChoices
|
|||||||
|
|
||||||
RACK_U_HEIGHT_DEFAULT = 42
|
RACK_U_HEIGHT_DEFAULT = 42
|
||||||
|
|
||||||
|
RACK_ELEVATION_LEGEND_WIDTH_DEFAULT = 30
|
||||||
|
|
||||||
RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 230
|
RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 230
|
||||||
RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 20
|
RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 20
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,7 @@
|
|||||||
from django.db.models import Manager, QuerySet
|
from django.db.models import Manager, QuerySet
|
||||||
from django.db.models.expressions import RawSQL
|
|
||||||
|
|
||||||
from .constants import NONCONNECTABLE_IFACE_TYPES
|
from .constants import NONCONNECTABLE_IFACE_TYPES
|
||||||
|
|
||||||
# Regular expressions for parsing Interface names
|
|
||||||
TYPE_RE = r"SUBSTRING({} FROM '^([^0-9\.:]+)')"
|
|
||||||
SLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(\d{{1,9}})/') AS integer), NULL)"
|
|
||||||
SUBSLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9\.:]+)?\d{{1,9}}/(\d{{1,9}})') AS integer), NULL)"
|
|
||||||
POSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}/){{2}}(\d{{1,9}})') AS integer), NULL)"
|
|
||||||
SUBPOSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}/){{3}}(\d{{1,9}})') AS integer), NULL)"
|
|
||||||
ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9\.:]+)?(\d{{1,9}})([^/]|$)') AS integer)"
|
|
||||||
CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^.*:(\d{{1,9}})(\.\d{{1,9}})?$') AS integer), 0)"
|
|
||||||
VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^.*\.(\d{{1,9}})$') AS integer), 0)"
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceQuerySet(QuerySet):
|
class InterfaceQuerySet(QuerySet):
|
||||||
|
|
||||||
@ -27,47 +16,4 @@ class InterfaceQuerySet(QuerySet):
|
|||||||
class InterfaceManager(Manager):
|
class InterfaceManager(Manager):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
return InterfaceQuerySet(self.model, using=self._db)
|
||||||
Naturally order interfaces by their type and numeric position. To order interfaces naturally, the `name` field
|
|
||||||
is split into eight distinct components: leading text (type), slot, subslot, position, subposition, ID, channel,
|
|
||||||
and virtual circuit:
|
|
||||||
|
|
||||||
{type}{slot or ID}/{subslot}/{position}/{subposition}:{channel}.{vc}
|
|
||||||
|
|
||||||
Components absent from the interface name are coalesced to zero or null. For example, an interface named
|
|
||||||
GigabitEthernet1/2/3 would be parsed as follows:
|
|
||||||
|
|
||||||
type = 'GigabitEthernet'
|
|
||||||
slot = 1
|
|
||||||
subslot = 2
|
|
||||||
position = 3
|
|
||||||
subposition = None
|
|
||||||
id = None
|
|
||||||
channel = 0
|
|
||||||
vc = 0
|
|
||||||
|
|
||||||
The original `name` field is considered in its entirety to serve as a fallback in the event interfaces do not
|
|
||||||
match any of the prescribed fields.
|
|
||||||
|
|
||||||
The `id` field is included to enforce deterministic ordering of interfaces in similar vein of other device
|
|
||||||
components.
|
|
||||||
"""
|
|
||||||
|
|
||||||
sql_col = '{}.name'.format(self.model._meta.db_table)
|
|
||||||
ordering = [
|
|
||||||
'_slot', '_subslot', '_position', '_subposition', '_type', '_id', '_channel', '_vc', 'name', 'pk'
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
fields = {
|
|
||||||
'_type': RawSQL(TYPE_RE.format(sql_col), []),
|
|
||||||
'_id': RawSQL(ID_RE.format(sql_col), []),
|
|
||||||
'_slot': RawSQL(SLOT_RE.format(sql_col), []),
|
|
||||||
'_subslot': RawSQL(SUBSLOT_RE.format(sql_col), []),
|
|
||||||
'_position': RawSQL(POSITION_RE.format(sql_col), []),
|
|
||||||
'_subposition': RawSQL(SUBPOSITION_RE.format(sql_col), []),
|
|
||||||
'_channel': RawSQL(CHANNEL_RE.format(sql_col), []),
|
|
||||||
'_vc': RawSQL(VC_RE.format(sql_col), []),
|
|
||||||
}
|
|
||||||
|
|
||||||
return InterfaceQuerySet(self.model, using=self._db).annotate(**fields).order_by(*ordering)
|
|
||||||
|
147
netbox/dcim/migrations/0093_device_component_ordering.py
Normal file
147
netbox/dcim/migrations/0093_device_component_ordering.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
import utilities.fields
|
||||||
|
import utilities.ordering
|
||||||
|
|
||||||
|
|
||||||
|
def _update_model_names(model):
|
||||||
|
# Update each unique field value in bulk
|
||||||
|
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, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_consoleports(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'ConsolePort'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_consoleserverports(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'ConsoleServerPort'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_powerports(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'PowerPort'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_poweroutlets(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'PowerOutlet'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_frontports(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'FrontPort'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_rearports(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'RearPort'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_devicebays(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'DeviceBay'))
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0092_fix_rack_outer_unit'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='consoleport',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='consoleserverport',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='devicebay',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='frontport',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='inventoryitem',
|
||||||
|
options={'ordering': ('device__id', 'parent__id', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='poweroutlet',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='powerport',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='rearport',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='consoleport',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='consoleserverport',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicebay',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='frontport',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='inventoryitem',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='poweroutlet',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='powerport',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rearport',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_consoleports,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_consoleserverports,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_powerports,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_poweroutlets,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_frontports,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_rearports,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_devicebays,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,138 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
import utilities.fields
|
||||||
|
import utilities.ordering
|
||||||
|
|
||||||
|
|
||||||
|
def _update_model_names(model):
|
||||||
|
# Update each unique field value in bulk
|
||||||
|
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, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_consoleporttemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'ConsolePortTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_consoleserverporttemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'ConsoleServerPortTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_powerporttemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'PowerPortTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_poweroutlettemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'PowerOutletTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_frontporttemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'FrontPortTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_rearporttemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'RearPortTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_devicebaytemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'DeviceBayTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0093_device_component_ordering'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='consoleporttemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='consoleserverporttemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='devicebaytemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='frontporttemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='poweroutlettemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='powerporttemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='rearporttemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='consoleporttemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='consoleserverporttemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicebaytemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='poweroutlettemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='powerporttemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rearporttemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_consoleporttemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_consoleserverporttemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_powerporttemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_poweroutlettemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_frontporttemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_rearporttemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_devicebaytemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
]
|
70
netbox/dcim/migrations/0095_primary_model_ordering.py
Normal file
70
netbox/dcim/migrations/0095_primary_model_ordering.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
import utilities.fields
|
||||||
|
import utilities.ordering
|
||||||
|
|
||||||
|
|
||||||
|
def _update_model_names(model):
|
||||||
|
# Update each unique field value in bulk
|
||||||
|
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, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_sites(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'Site'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_racks(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'Rack'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_devices(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'Device'))
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0094_device_component_template_ordering'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='device',
|
||||||
|
options={'ordering': ('_name', 'pk'), 'permissions': (('napalm_read', 'Read-only access to devices via NAPALM'), ('napalm_write', 'Read/write access to devices via NAPALM'))},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='rack',
|
||||||
|
options={'ordering': ('site', 'group', '_name', 'pk')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='site',
|
||||||
|
options={'ordering': ('_name',)},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='device',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='rack',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='site',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_sites,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_racks,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_devices,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
]
|
53
netbox/dcim/migrations/0096_interface_ordering.py
Normal file
53
netbox/dcim/migrations/0096_interface_ordering.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
import utilities.fields
|
||||||
|
import utilities.ordering
|
||||||
|
|
||||||
|
|
||||||
|
def _update_model_names(model):
|
||||||
|
# Update each unique field value in bulk
|
||||||
|
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, max_length=100))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_interfacetemplates(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'InterfaceTemplate'))
|
||||||
|
|
||||||
|
|
||||||
|
def naturalize_interfaces(apps, schema_editor):
|
||||||
|
_update_model_names(apps.get_model('dcim', 'Interface'))
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0095_primary_model_ordering'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='interface',
|
||||||
|
options={'ordering': ('device', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='interfacetemplate',
|
||||||
|
options={'ordering': ('device_type', '_name')},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interface',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interfacetemplate',
|
||||||
|
name='_name',
|
||||||
|
field=utilities.fields.NaturalOrderingField('target_field', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_interfacetemplates,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=naturalize_interfaces,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
]
|
@ -22,8 +22,7 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.fields import ASNField
|
from dcim.fields import ASNField
|
||||||
from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
|
from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
|
||||||
from utilities.fields import ColorField
|
from utilities.fields import ColorField, NaturalOrderingField
|
||||||
from utilities.managers import NaturalOrderingManager
|
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from utilities.utils import foreground_color, to_meters
|
from utilities.utils import foreground_color, to_meters
|
||||||
from .device_component_templates import (
|
from .device_component_templates import (
|
||||||
@ -134,6 +133,11 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
slug = models.SlugField(
|
slug = models.SlugField(
|
||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
@ -215,8 +219,6 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
images = GenericRelation(
|
images = GenericRelation(
|
||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -235,7 +237,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ('_name',)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -380,13 +382,17 @@ 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
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _draw_device_front(drawing, device, start, end, text):
|
def _draw_device_front(drawing, device, start, end, text):
|
||||||
|
name = str(device)
|
||||||
|
if device.devicebay_count:
|
||||||
|
name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count)
|
||||||
|
|
||||||
color = device.device_role.color
|
color = device.device_role.color
|
||||||
link = drawing.add(
|
link = drawing.add(
|
||||||
drawing.a(
|
drawing.a(
|
||||||
@ -401,7 +407,7 @@ class RackElevationHelperMixin:
|
|||||||
))
|
))
|
||||||
link.add(drawing.rect(start, end, style='fill: #{}'.format(color), class_='slot'))
|
link.add(drawing.rect(start, end, style='fill: #{}'.format(color), class_='slot'))
|
||||||
hex_color = '#{}'.format(foreground_color(color))
|
hex_color = '#{}'.format(foreground_color(color))
|
||||||
link.add(drawing.text(str(device), insert=text, fill=hex_color))
|
link.add(drawing.text(str(name), insert=text, fill=hex_color))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _draw_device_rear(drawing, device, start, end, text):
|
def _draw_device_rear(drawing, device, start, end, text):
|
||||||
@ -431,11 +437,19 @@ class RackElevationHelperMixin:
|
|||||||
link.add(drawing.rect(start, end, class_=class_))
|
link.add(drawing.rect(start, end, class_=class_))
|
||||||
link.add(drawing.text("add device", insert=text, class_='add-device'))
|
link.add(drawing.text("add device", insert=text, class_='add-device'))
|
||||||
|
|
||||||
def _draw_elevations(self, elevation, reserved_units, face, unit_width, unit_height):
|
def _draw_elevations(self, elevation, reserved_units, face, unit_width, unit_height, legend_width):
|
||||||
|
|
||||||
drawing = self._setup_drawing(unit_width, unit_height * self.u_height)
|
drawing = self._setup_drawing(unit_width + legend_width, unit_height * self.u_height)
|
||||||
|
|
||||||
unit_cursor = 0
|
unit_cursor = 0
|
||||||
|
for ru in range(0, self.u_height):
|
||||||
|
start_y = ru * unit_height
|
||||||
|
position_coordinates = (legend_width / 2, start_y + unit_height / 2 + 2)
|
||||||
|
unit = ru + 1 if self.desc_units else self.u_height - ru
|
||||||
|
drawing.add(
|
||||||
|
drawing.text(str(unit), position_coordinates, class_="unit")
|
||||||
|
)
|
||||||
|
|
||||||
for unit in elevation:
|
for unit in elevation:
|
||||||
|
|
||||||
# Loop through all units in the elevation
|
# Loop through all units in the elevation
|
||||||
@ -445,9 +459,9 @@ class RackElevationHelperMixin:
|
|||||||
# Setup drawing coordinates
|
# Setup drawing coordinates
|
||||||
start_y = unit_cursor * unit_height
|
start_y = unit_cursor * unit_height
|
||||||
end_y = unit_height * height
|
end_y = unit_height * height
|
||||||
start_cordinates = (0, start_y)
|
start_cordinates = (legend_width, start_y)
|
||||||
end_cordinates = (unit_width, end_y)
|
end_cordinates = (legend_width + unit_width, end_y)
|
||||||
text_cordinates = (unit_width / 2, start_y + end_y / 2)
|
text_cordinates = (legend_width + (unit_width / 2), start_y + end_y / 2)
|
||||||
|
|
||||||
# Draw the device
|
# Draw the device
|
||||||
if device and device.face == face:
|
if device and device.face == face:
|
||||||
@ -469,7 +483,7 @@ class RackElevationHelperMixin:
|
|||||||
unit_cursor += height
|
unit_cursor += height
|
||||||
|
|
||||||
# Wrap the drawing with a border
|
# Wrap the drawing with a border
|
||||||
drawing.add(drawing.rect((0, 0), (unit_width, self.u_height * unit_height), class_='rack'))
|
drawing.add(drawing.rect((legend_width, 0), (unit_width, self.u_height * unit_height), class_='rack'))
|
||||||
|
|
||||||
return drawing
|
return drawing
|
||||||
|
|
||||||
@ -492,7 +506,8 @@ class RackElevationHelperMixin:
|
|||||||
self,
|
self,
|
||||||
face=DeviceFaceChoices.FACE_FRONT,
|
face=DeviceFaceChoices.FACE_FRONT,
|
||||||
unit_width=RACK_ELEVATION_UNIT_WIDTH_DEFAULT,
|
unit_width=RACK_ELEVATION_UNIT_WIDTH_DEFAULT,
|
||||||
unit_height=RACK_ELEVATION_UNIT_HEIGHT_DEFAULT
|
unit_height=RACK_ELEVATION_UNIT_HEIGHT_DEFAULT,
|
||||||
|
legend_width=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Return an SVG of the rack elevation
|
Return an SVG of the rack elevation
|
||||||
@ -505,7 +520,7 @@ class RackElevationHelperMixin:
|
|||||||
elevation = self.merge_elevations(face)
|
elevation = self.merge_elevations(face)
|
||||||
reserved_units = self.get_reserved_units()
|
reserved_units = self.get_reserved_units()
|
||||||
|
|
||||||
return self._draw_elevations(elevation, reserved_units, face, unit_width, unit_height)
|
return self._draw_elevations(elevation, reserved_units, face, unit_width, unit_height, legend_width)
|
||||||
|
|
||||||
|
|
||||||
class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
||||||
@ -516,6 +531,11 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
facility_id = models.CharField(
|
facility_id = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -612,8 +632,6 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
|||||||
images = GenericRelation(
|
images = GenericRelation(
|
||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -634,12 +652,12 @@ class Rack(ChangeLoggedModel, CustomFieldModel, RackElevationHelperMixin):
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('site', 'group', 'name', 'pk') # (site, group, name) may be non-unique
|
ordering = ('site', 'group', '_name', 'pk') # (site, group, name) may be non-unique
|
||||||
unique_together = [
|
unique_together = (
|
||||||
# Name and facility_id must be unique *only* within a RackGroup
|
# Name and facility_id must be unique *only* within a RackGroup
|
||||||
['group', 'name'],
|
('group', 'name'),
|
||||||
['group', 'facility_id'],
|
('group', 'facility_id'),
|
||||||
]
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.display_name or super().__str__()
|
return self.display_name or super().__str__()
|
||||||
@ -1313,6 +1331,12 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -1407,8 +1431,6 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
images = GenericRelation(
|
images = GenericRelation(
|
||||||
to='extras.ImageAttachment'
|
to='extras.ImageAttachment'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -1430,12 +1452,12 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name', 'pk') # Name may be NULL
|
ordering = ('_name', 'pk') # Name may be null
|
||||||
unique_together = [
|
unique_together = (
|
||||||
['site', 'tenant', 'name'], # See validate_unique below
|
('site', 'tenant', 'name'), # See validate_unique below
|
||||||
['rack', 'position', 'face'],
|
('rack', 'position', 'face'),
|
||||||
['virtual_chassis', 'vc_position'],
|
('virtual_chassis', 'vc_position'),
|
||||||
]
|
)
|
||||||
permissions = (
|
permissions = (
|
||||||
('napalm_read', 'Read-only access to devices via NAPALM'),
|
('napalm_read', 'Read-only access to devices via NAPALM'),
|
||||||
('napalm_write', 'Read/write access to devices via NAPALM'),
|
('napalm_write', 'Read/write access to devices via NAPALM'),
|
||||||
|
@ -4,9 +4,9 @@ from django.db import models
|
|||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.managers import InterfaceManager
|
|
||||||
from extras.models import ObjectChange
|
from extras.models import ObjectChange
|
||||||
from utilities.managers import NaturalOrderingManager
|
from utilities.fields import NaturalOrderingField
|
||||||
|
from utilities.ordering import naturalize_interface
|
||||||
from utilities.utils import serialize_object
|
from utilities.utils import serialize_object
|
||||||
from .device_components import (
|
from .device_components import (
|
||||||
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, PowerOutlet, PowerPort, RearPort,
|
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, PowerOutlet, PowerPort, RearPort,
|
||||||
@ -58,17 +58,20 @@ class ConsolePortTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -93,17 +96,20 @@ class ConsoleServerPortTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -128,6 +134,11 @@ class PowerPortTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
@ -146,11 +157,9 @@ class PowerPortTemplate(ComponentTemplateModel):
|
|||||||
help_text="Allocated power draw (watts)"
|
help_text="Allocated power draw (watts)"
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -159,6 +168,7 @@ class PowerPortTemplate(ComponentTemplateModel):
|
|||||||
return PowerPort(
|
return PowerPort(
|
||||||
device=device,
|
device=device,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
|
type=self.type,
|
||||||
maximum_draw=self.maximum_draw,
|
maximum_draw=self.maximum_draw,
|
||||||
allocated_draw=self.allocated_draw
|
allocated_draw=self.allocated_draw
|
||||||
)
|
)
|
||||||
@ -176,6 +186,11 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
@ -195,11 +210,9 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
|||||||
help_text="Phase (for three-phase feeds)"
|
help_text="Phase (for three-phase feeds)"
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -220,6 +233,7 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
|||||||
return PowerOutlet(
|
return PowerOutlet(
|
||||||
device=device,
|
device=device,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
|
type=self.type,
|
||||||
power_port=power_port,
|
power_port=power_port,
|
||||||
feed_leg=self.feed_leg
|
feed_leg=self.feed_leg
|
||||||
)
|
)
|
||||||
@ -237,6 +251,12 @@ class InterfaceTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
naturalize_function=naturalize_interface,
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfaceTypeChoices
|
choices=InterfaceTypeChoices
|
||||||
@ -246,11 +266,9 @@ class InterfaceTemplate(ComponentTemplateModel):
|
|||||||
verbose_name='Management only'
|
verbose_name='Management only'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = InterfaceManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -276,6 +294,11 @@ class FrontPortTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -290,14 +313,12 @@ class FrontPortTemplate(ComponentTemplateModel):
|
|||||||
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = [
|
unique_together = (
|
||||||
['device_type', 'name'],
|
('device_type', 'name'),
|
||||||
['rear_port', 'rear_port_position'],
|
('rear_port', 'rear_port_position'),
|
||||||
]
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -344,6 +365,11 @@ class RearPortTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -353,11 +379,9 @@ class RearPortTemplate(ComponentTemplateModel):
|
|||||||
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -383,12 +407,15 @@ class DeviceBayTemplate(ComponentTemplateModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
objects = NaturalOrderingManager()
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device_type', 'name']
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ['device_type', 'name']
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -10,9 +10,9 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.exceptions import LoopDetected
|
from dcim.exceptions import LoopDetected
|
||||||
from dcim.fields import MACAddressField
|
from dcim.fields import MACAddressField
|
||||||
from dcim.managers import InterfaceManager
|
|
||||||
from extras.models import ObjectChange, TaggedItem
|
from extras.models import ObjectChange, TaggedItem
|
||||||
from utilities.managers import NaturalOrderingManager
|
from utilities.fields import NaturalOrderingField
|
||||||
|
from utilities.ordering import naturalize_interface
|
||||||
from utilities.utils import serialize_object
|
from utilities.utils import serialize_object
|
||||||
from virtualization.choices import VMInterfaceTypeChoices
|
from virtualization.choices import VMInterfaceTypeChoices
|
||||||
|
|
||||||
@ -181,6 +181,11 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
@ -197,15 +202,13 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'description']
|
csv_headers = ['device', 'name', 'type', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
unique_together = ['device', 'name']
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -238,6 +241,11 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
@ -247,14 +255,13 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'description']
|
csv_headers = ['device', 'name', 'type', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -287,6 +294,11 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
@ -322,15 +334,13 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description']
|
csv_headers = ['device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
unique_together = ['device', 'name']
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -433,6 +443,11 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50
|
max_length=50
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
@ -455,14 +470,13 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'power_port', 'feed_leg', 'description']
|
csv_headers = ['device', 'name', 'type', 'power_port', 'feed_leg', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -515,6 +529,12 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
naturalize_function=naturalize_interface,
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
_connected_interface = models.OneToOneField(
|
_connected_interface = models.OneToOneField(
|
||||||
to='self',
|
to='self',
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
@ -583,8 +603,6 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Tagged VLANs'
|
verbose_name='Tagged VLANs'
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = InterfaceManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
@ -593,8 +611,9 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
# TODO: ordering and unique_together should include virtual_machine
|
||||||
unique_together = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -761,6 +780,11 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -774,20 +798,17 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
default=1,
|
default=1,
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
||||||
)
|
)
|
||||||
|
|
||||||
is_path_endpoint = False
|
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
|
csv_headers = ['device', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
|
||||||
|
is_path_endpoint = False
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
unique_together = [
|
unique_together = (
|
||||||
['device', 'name'],
|
('device', 'name'),
|
||||||
['rear_port', 'rear_port_position'],
|
('rear_port', 'rear_port_position'),
|
||||||
]
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -831,6 +852,11 @@ class RearPort(CableTermination, ComponentModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -839,17 +865,14 @@ class RearPort(CableTermination, ComponentModel):
|
|||||||
default=1,
|
default=1,
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
validators=[MinValueValidator(1), MaxValueValidator(64)]
|
||||||
)
|
)
|
||||||
|
|
||||||
is_path_endpoint = False
|
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'positions', 'description']
|
csv_headers = ['device', 'name', 'type', 'positions', 'description']
|
||||||
|
is_path_endpoint = False
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
unique_together = ['device', 'name']
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -881,6 +904,11 @@ class DeviceBay(ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Name'
|
verbose_name='Name'
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
installed_device = models.OneToOneField(
|
installed_device = models.OneToOneField(
|
||||||
to='dcim.Device',
|
to='dcim.Device',
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
@ -888,15 +916,13 @@ class DeviceBay(ComponentModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'installed_device', 'description']
|
csv_headers = ['device', 'name', 'installed_device', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ('device', '_name')
|
||||||
unique_together = ['device', 'name']
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{} - {}'.format(self.device.name, self.name)
|
return '{} - {}'.format(self.device.name, self.name)
|
||||||
@ -960,6 +986,11 @@ class InventoryItem(ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Name'
|
verbose_name='Name'
|
||||||
)
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
manufacturer = models.ForeignKey(
|
manufacturer = models.ForeignKey(
|
||||||
to='dcim.Manufacturer',
|
to='dcim.Manufacturer',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -997,14 +1028,14 @@ class InventoryItem(ComponentModel):
|
|||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device__id', 'parent__id', 'name']
|
ordering = ('device__id', 'parent__id', '_name')
|
||||||
unique_together = ['device', 'parent', 'name']
|
unique_together = ('device', 'parent', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return self.device.get_absolute_url()
|
return reverse('dcim:device_inventory', kwargs={'pk': self.device.pk})
|
||||||
|
|
||||||
def to_csv(self):
|
def to_csv(self):
|
||||||
return (
|
return (
|
||||||
|
@ -200,6 +200,11 @@ def get_component_template_actions(model_name):
|
|||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{{% endif %}}
|
{{% endif %}}
|
||||||
|
{{% if perms.dcim.delete_{model_name} %}}
|
||||||
|
<a href="{{% url 'dcim:{model_name}_delete' pk=record.pk %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-danger">
|
||||||
|
<i class="glyphicon glyphicon-trash" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
{{% endif %}}
|
||||||
""".format(model_name=model_name).strip()
|
""".format(model_name=model_name).strip()
|
||||||
|
|
||||||
|
|
||||||
@ -229,7 +234,7 @@ class RegionTable(BaseTable):
|
|||||||
|
|
||||||
class SiteTable(BaseTable):
|
class SiteTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
|
name = tables.LinkColumn(order_by=('_name',))
|
||||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||||
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
@ -291,7 +296,7 @@ class RackRoleTable(BaseTable):
|
|||||||
|
|
||||||
class RackTable(BaseTable):
|
class RackTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
|
name = tables.LinkColumn(order_by=('_name',))
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
@ -409,6 +414,7 @@ class DeviceTypeTable(BaseTable):
|
|||||||
|
|
||||||
class ConsolePortTemplateTable(BaseTable):
|
class ConsolePortTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('consoleporttemplate'),
|
template_code=get_component_template_actions('consoleporttemplate'),
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
@ -432,6 +438,7 @@ class ConsolePortImportTable(BaseTable):
|
|||||||
|
|
||||||
class ConsoleServerPortTemplateTable(BaseTable):
|
class ConsoleServerPortTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('consoleserverporttemplate'),
|
template_code=get_component_template_actions('consoleserverporttemplate'),
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
@ -440,7 +447,7 @@ class ConsoleServerPortTemplateTable(BaseTable):
|
|||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = ConsoleServerPortTemplate
|
model = ConsoleServerPortTemplate
|
||||||
fields = ('pk', 'name', 'actions')
|
fields = ('pk', 'name', 'type', 'actions')
|
||||||
empty_text = "None"
|
empty_text = "None"
|
||||||
|
|
||||||
|
|
||||||
@ -455,6 +462,7 @@ class ConsoleServerPortImportTable(BaseTable):
|
|||||||
|
|
||||||
class PowerPortTemplateTable(BaseTable):
|
class PowerPortTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('powerporttemplate'),
|
template_code=get_component_template_actions('powerporttemplate'),
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
@ -478,6 +486,7 @@ class PowerPortImportTable(BaseTable):
|
|||||||
|
|
||||||
class PowerOutletTemplateTable(BaseTable):
|
class PowerOutletTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('poweroutlettemplate'),
|
template_code=get_component_template_actions('poweroutlettemplate'),
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
@ -526,6 +535,7 @@ class InterfaceImportTable(BaseTable):
|
|||||||
|
|
||||||
class FrontPortTemplateTable(BaseTable):
|
class FrontPortTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
rear_port_position = tables.Column(
|
rear_port_position = tables.Column(
|
||||||
verbose_name='Position'
|
verbose_name='Position'
|
||||||
)
|
)
|
||||||
@ -552,6 +562,7 @@ class FrontPortImportTable(BaseTable):
|
|||||||
|
|
||||||
class RearPortTemplateTable(BaseTable):
|
class RearPortTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('rearporttemplate'),
|
template_code=get_component_template_actions('rearporttemplate'),
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
@ -575,6 +586,7 @@ class RearPortImportTable(BaseTable):
|
|||||||
|
|
||||||
class DeviceBayTemplateTable(BaseTable):
|
class DeviceBayTemplateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('devicebaytemplate'),
|
template_code=get_component_template_actions('devicebaytemplate'),
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
attrs={'td': {'class': 'text-right noprint'}},
|
||||||
@ -654,7 +666,7 @@ class PlatformTable(BaseTable):
|
|||||||
class DeviceTable(BaseTable):
|
class DeviceTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.TemplateColumn(
|
name = tables.TemplateColumn(
|
||||||
order_by=('_nat1', '_nat2', '_nat3'),
|
order_by=('_name',),
|
||||||
template_code=DEVICE_LINK
|
template_code=DEVICE_LINK
|
||||||
)
|
)
|
||||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||||
@ -704,6 +716,7 @@ class DeviceImportTable(BaseTable):
|
|||||||
|
|
||||||
class DeviceComponentDetailTable(BaseTable):
|
class DeviceComponentDetailTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
cable = tables.LinkColumn()
|
cable = tables.LinkColumn()
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -713,6 +726,7 @@ class DeviceComponentDetailTable(BaseTable):
|
|||||||
|
|
||||||
|
|
||||||
class ConsolePortTable(BaseTable):
|
class ConsolePortTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
@ -727,6 +741,7 @@ class ConsolePortDetailTable(DeviceComponentDetailTable):
|
|||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTable(BaseTable):
|
class ConsoleServerPortTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
@ -741,6 +756,7 @@ class ConsoleServerPortDetailTable(DeviceComponentDetailTable):
|
|||||||
|
|
||||||
|
|
||||||
class PowerPortTable(BaseTable):
|
class PowerPortTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
@ -755,6 +771,7 @@ class PowerPortDetailTable(DeviceComponentDetailTable):
|
|||||||
|
|
||||||
|
|
||||||
class PowerOutletTable(BaseTable):
|
class PowerOutletTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
@ -777,6 +794,7 @@ class InterfaceTable(BaseTable):
|
|||||||
|
|
||||||
class InterfaceDetailTable(DeviceComponentDetailTable):
|
class InterfaceDetailTable(DeviceComponentDetailTable):
|
||||||
parent = tables.LinkColumn(order_by=('device', 'virtual_machine'))
|
parent = tables.LinkColumn(order_by=('device', 'virtual_machine'))
|
||||||
|
name = tables.LinkColumn()
|
||||||
|
|
||||||
class Meta(InterfaceTable.Meta):
|
class Meta(InterfaceTable.Meta):
|
||||||
order_by = ('parent', 'name')
|
order_by = ('parent', 'name')
|
||||||
@ -785,6 +803,7 @@ class InterfaceDetailTable(DeviceComponentDetailTable):
|
|||||||
|
|
||||||
|
|
||||||
class FrontPortTable(BaseTable):
|
class FrontPortTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
@ -800,6 +819,7 @@ class FrontPortDetailTable(DeviceComponentDetailTable):
|
|||||||
|
|
||||||
|
|
||||||
class RearPortTable(BaseTable):
|
class RearPortTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = RearPort
|
model = RearPort
|
||||||
@ -815,6 +835,7 @@ class RearPortDetailTable(DeviceComponentDetailTable):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceBayTable(BaseTable):
|
class DeviceBayTable(BaseTable):
|
||||||
|
name = tables.Column(order_by=('_name',))
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
|
@ -11,7 +11,7 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from utilities.testing import StandardTestCases
|
from utilities.testing import ViewTestCases
|
||||||
|
|
||||||
|
|
||||||
def create_test_device(name):
|
def create_test_device(name):
|
||||||
@ -27,14 +27,9 @@ def create_test_device(name):
|
|||||||
return device
|
return device
|
||||||
|
|
||||||
|
|
||||||
class RegionTestCase(StandardTestCases.Views):
|
class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = Region
|
model = Region
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -61,7 +56,7 @@ class RegionTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SiteTestCase(StandardTestCases.Views):
|
class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Site
|
model = Site
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -118,14 +113,9 @@ class SiteTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RackGroupTestCase(StandardTestCases.Views):
|
class RackGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = RackGroup
|
model = RackGroup
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -152,14 +142,9 @@ class RackGroupTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RackRoleTestCase(StandardTestCases.Views):
|
class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = RackRole
|
model = RackRole
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -184,7 +169,7 @@ class RackRoleTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RackReservationTestCase(StandardTestCases.Views):
|
class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = RackReservation
|
model = RackReservation
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
@ -226,7 +211,7 @@ class RackReservationTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RackTestCase(StandardTestCases.Views):
|
class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Rack
|
model = Rack
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -302,14 +287,9 @@ class RackTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerTestCase(StandardTestCases.Views):
|
class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -332,7 +312,7 @@ class ManufacturerTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeTestCase(StandardTestCases.Views):
|
class DeviceTypeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -524,14 +504,318 @@ device-bays:
|
|||||||
self.assertEqual(data[0]['model'], 'Device Type 1')
|
self.assertEqual(data[0]['model'], 'Device Type 1')
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleTestCase(StandardTestCases.Views):
|
#
|
||||||
model = DeviceRole
|
# DeviceType components
|
||||||
|
#
|
||||||
|
|
||||||
# Disable inapplicable tests
|
class ConsolePortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
test_get_object = None
|
model = ConsolePortTemplate
|
||||||
test_delete_object = None
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
ConsolePortTemplate.objects.bulk_create((
|
||||||
|
ConsolePortTemplate(device_type=devicetypes[0], name='Console Port Template 1'),
|
||||||
|
ConsolePortTemplate(device_type=devicetypes[0], name='Console Port Template 2'),
|
||||||
|
ConsolePortTemplate(device_type=devicetypes[0], name='Console Port Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Console Port Template X',
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Console Port Template [4-6]',
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ConsoleServerPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = ConsoleServerPortTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
ConsoleServerPortTemplate.objects.bulk_create((
|
||||||
|
ConsoleServerPortTemplate(device_type=devicetypes[0], name='Console Server Port Template 1'),
|
||||||
|
ConsoleServerPortTemplate(device_type=devicetypes[0], name='Console Server Port Template 2'),
|
||||||
|
ConsoleServerPortTemplate(device_type=devicetypes[0], name='Console Server Port Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Console Server Port Template X',
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Console Server Port Template [4-6]',
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PowerPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = PowerPortTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
PowerPortTemplate.objects.bulk_create((
|
||||||
|
PowerPortTemplate(device_type=devicetypes[0], name='Power Port Template 1'),
|
||||||
|
PowerPortTemplate(device_type=devicetypes[0], name='Power Port Template 2'),
|
||||||
|
PowerPortTemplate(device_type=devicetypes[0], name='Power Port Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Power Port Template X',
|
||||||
|
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||||
|
'maximum_draw': 100,
|
||||||
|
'allocated_draw': 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Power Port Template [4-6]',
|
||||||
|
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||||
|
'maximum_draw': 100,
|
||||||
|
'allocated_draw': 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||||
|
'maximum_draw': 100,
|
||||||
|
'allocated_draw': 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PowerOutletTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = PowerOutletTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
|
||||||
|
|
||||||
|
PowerOutletTemplate.objects.bulk_create((
|
||||||
|
PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 1'),
|
||||||
|
PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 2'),
|
||||||
|
PowerOutletTemplate(device_type=devicetype, name='Power Outlet Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
powerports = (
|
||||||
|
PowerPortTemplate(device_type=devicetype, name='Power Port Template 1'),
|
||||||
|
)
|
||||||
|
PowerPortTemplate.objects.bulk_create(powerports)
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name': 'Power Outlet Template X',
|
||||||
|
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||||
|
'power_port': powerports[0].pk,
|
||||||
|
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name_pattern': 'Power Outlet Template [4-6]',
|
||||||
|
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||||
|
'power_port': powerports[0].pk,
|
||||||
|
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||||
|
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = InterfaceTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
InterfaceTemplate.objects.bulk_create((
|
||||||
|
InterfaceTemplate(device_type=devicetypes[0], name='Interface Template 1'),
|
||||||
|
InterfaceTemplate(device_type=devicetypes[0], name='Interface Template 2'),
|
||||||
|
InterfaceTemplate(device_type=devicetypes[0], name='Interface Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Interface Template X',
|
||||||
|
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||||
|
'mgmt_only': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Interface Template [4-6]',
|
||||||
|
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||||
|
'mgmt_only': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||||
|
'mgmt_only': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = FrontPortTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetype = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
|
||||||
|
|
||||||
|
rearports = (
|
||||||
|
RearPortTemplate(device_type=devicetype, name='Rear Port Template 1'),
|
||||||
|
RearPortTemplate(device_type=devicetype, name='Rear Port Template 2'),
|
||||||
|
RearPortTemplate(device_type=devicetype, name='Rear Port Template 3'),
|
||||||
|
RearPortTemplate(device_type=devicetype, name='Rear Port Template 4'),
|
||||||
|
RearPortTemplate(device_type=devicetype, name='Rear Port Template 5'),
|
||||||
|
RearPortTemplate(device_type=devicetype, name='Rear Port Template 6'),
|
||||||
|
)
|
||||||
|
RearPortTemplate.objects.bulk_create(rearports)
|
||||||
|
|
||||||
|
FrontPortTemplate.objects.bulk_create((
|
||||||
|
FrontPortTemplate(device_type=devicetype, name='Front Port Template 1', rear_port=rearports[0], rear_port_position=1),
|
||||||
|
FrontPortTemplate(device_type=devicetype, name='Front Port Template 2', rear_port=rearports[1], rear_port_position=1),
|
||||||
|
FrontPortTemplate(device_type=devicetype, name='Front Port Template 3', rear_port=rearports[2], rear_port_position=1),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name': 'Front Port X',
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'rear_port': rearports[3].pk,
|
||||||
|
'rear_port_position': 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name_pattern': 'Front Port [4-6]',
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'rear_port_set': [
|
||||||
|
'{}:1'.format(rp.pk) for rp in rearports[3:6]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RearPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = RearPortTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
RearPortTemplate.objects.bulk_create((
|
||||||
|
RearPortTemplate(device_type=devicetypes[0], name='Rear Port Template 1'),
|
||||||
|
RearPortTemplate(device_type=devicetypes[0], name='Rear Port Template 2'),
|
||||||
|
RearPortTemplate(device_type=devicetypes[0], name='Rear Port Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Rear Port Template X',
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'positions': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Rear Port Template [4-6]',
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'positions': 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = DeviceBayTemplate
|
||||||
|
|
||||||
|
# Disable inapplicable views
|
||||||
test_bulk_edit_objects = None
|
test_bulk_edit_objects = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
DeviceBayTemplate.objects.bulk_create((
|
||||||
|
DeviceBayTemplate(device_type=devicetypes[0], name='Device Bay Template 1'),
|
||||||
|
DeviceBayTemplate(device_type=devicetypes[0], name='Device Bay Template 2'),
|
||||||
|
DeviceBayTemplate(device_type=devicetypes[0], name='Device Bay Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Device Bay Template X',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Device Bay Template [4-6]',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
|
model = DeviceRole
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -557,14 +841,9 @@ class DeviceRoleTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlatformTestCase(StandardTestCases.Views):
|
class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = Platform
|
model = Platform
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -592,7 +871,7 @@ class PlatformTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceTestCase(StandardTestCases.Views):
|
class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Device
|
model = Device
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -677,17 +956,9 @@ class DeviceTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortTestCase(StandardTestCases.Views):
|
class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -704,11 +975,19 @@ class ConsolePortTestCase(StandardTestCases.Views):
|
|||||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
'description': 'A console port',
|
'description': 'A console port',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'cable': None,
|
'device': device.pk,
|
||||||
'connected_endpoint': None,
|
'name_pattern': 'Console Port [4-6]',
|
||||||
'connection_status': None,
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
'description': 'A console port',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -719,17 +998,9 @@ class ConsolePortTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTestCase(StandardTestCases.Views):
|
class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -746,10 +1017,20 @@ class ConsoleServerPortTestCase(StandardTestCases.Views):
|
|||||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
'description': 'A console server port',
|
'description': 'A console server port',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'cable': None,
|
'device': device.pk,
|
||||||
'connection_status': None,
|
'name_pattern': 'Console Server Port [4-6]',
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
'description': 'A console server port',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'device': device.pk,
|
||||||
|
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||||
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -760,17 +1041,9 @@ class ConsoleServerPortTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTestCase(StandardTestCases.Views):
|
class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -789,10 +1062,23 @@ class PowerPortTestCase(StandardTestCases.Views):
|
|||||||
'allocated_draw': 50,
|
'allocated_draw': 50,
|
||||||
'description': 'A power port',
|
'description': 'A power port',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'cable': None,
|
'device': device.pk,
|
||||||
'connection_status': None,
|
'name_pattern': 'Power Port [4-6]]',
|
||||||
|
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||||
|
'maximum_draw': 100,
|
||||||
|
'allocated_draw': 50,
|
||||||
|
'description': 'A power port',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||||
|
'maximum_draw': 100,
|
||||||
|
'allocated_draw': 50,
|
||||||
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -803,17 +1089,9 @@ class PowerPortTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTestCase(StandardTestCases.Views):
|
class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -838,10 +1116,24 @@ class PowerOutletTestCase(StandardTestCases.Views):
|
|||||||
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||||
'description': 'A power outlet',
|
'description': 'A power outlet',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'cable': None,
|
'device': device.pk,
|
||||||
'connection_status': None,
|
'name_pattern': 'Power Outlet [4-6]',
|
||||||
|
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||||
|
'power_port': powerports[1].pk,
|
||||||
|
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||||
|
'description': 'A power outlet',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'device': device.pk,
|
||||||
|
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
|
||||||
|
'power_port': powerports[1].pk,
|
||||||
|
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||||
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -852,23 +1144,23 @@ class PowerOutletTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTestCase(StandardTestCases.Views):
|
class InterfaceTestCase(
|
||||||
|
ViewTestCases.GetObjectViewTestCase,
|
||||||
|
ViewTestCases.DeviceComponentViewTestCase,
|
||||||
|
):
|
||||||
model = Interface
|
model = Interface
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
|
|
||||||
Interface.objects.bulk_create([
|
interfaces = (
|
||||||
Interface(device=device, name='Interface 1'),
|
Interface(device=device, name='Interface 1'),
|
||||||
Interface(device=device, name='Interface 2'),
|
Interface(device=device, name='Interface 2'),
|
||||||
Interface(device=device, name='Interface 3'),
|
Interface(device=device, name='Interface 3'),
|
||||||
])
|
Interface(device=device, name='LAG', type=InterfaceTypeChoices.TYPE_LAG),
|
||||||
|
)
|
||||||
|
Interface.objects.bulk_create(interfaces)
|
||||||
|
|
||||||
vlans = (
|
vlans = (
|
||||||
VLAN(vid=1, name='VLAN1', site=device.site),
|
VLAN(vid=1, name='VLAN1', site=device.site),
|
||||||
@ -884,7 +1176,38 @@ class InterfaceTestCase(StandardTestCases.Views):
|
|||||||
'name': 'Interface X',
|
'name': 'Interface X',
|
||||||
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||||
'enabled': False,
|
'enabled': False,
|
||||||
'lag': None,
|
'lag': interfaces[3].pk,
|
||||||
|
'mac_address': EUI('01:02:03:04:05:06'),
|
||||||
|
'mtu': 2000,
|
||||||
|
'mgmt_only': True,
|
||||||
|
'description': 'A front port',
|
||||||
|
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||||
|
'untagged_vlan': vlans[0].pk,
|
||||||
|
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device': device.pk,
|
||||||
|
'name_pattern': 'Interface [4-6]',
|
||||||
|
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||||
|
'enabled': False,
|
||||||
|
'lag': interfaces[3].pk,
|
||||||
|
'mac_address': EUI('01:02:03:04:05:06'),
|
||||||
|
'mtu': 2000,
|
||||||
|
'mgmt_only': True,
|
||||||
|
'description': 'A front port',
|
||||||
|
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||||
|
'untagged_vlan': vlans[0].pk,
|
||||||
|
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'device': device.pk,
|
||||||
|
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||||
|
'enabled': False,
|
||||||
|
'lag': interfaces[3].pk,
|
||||||
'mac_address': EUI('01:02:03:04:05:06'),
|
'mac_address': EUI('01:02:03:04:05:06'),
|
||||||
'mtu': 2000,
|
'mtu': 2000,
|
||||||
'mgmt_only': True,
|
'mgmt_only': True,
|
||||||
@ -892,11 +1215,6 @@ class InterfaceTestCase(StandardTestCases.Views):
|
|||||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||||
'untagged_vlan': vlans[0].pk,
|
'untagged_vlan': vlans[0].pk,
|
||||||
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
'tagged_vlans': [v.pk for v in vlans[1:4]],
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
|
||||||
|
|
||||||
# Extraneous model fields
|
|
||||||
'cable': None,
|
|
||||||
'connection_status': None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -907,17 +1225,9 @@ class InterfaceTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTestCase(StandardTestCases.Views):
|
class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -946,9 +1256,22 @@ class FrontPortTestCase(StandardTestCases.Views):
|
|||||||
'rear_port_position': 1,
|
'rear_port_position': 1,
|
||||||
'description': 'New description',
|
'description': 'New description',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'cable': None,
|
'device': device.pk,
|
||||||
|
'name_pattern': 'Front Port [4-6]',
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'rear_port_set': [
|
||||||
|
'{}:1'.format(rp.pk) for rp in rearports[3:6]
|
||||||
|
],
|
||||||
|
'description': 'New description',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -959,17 +1282,9 @@ class FrontPortTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RearPortTestCase(StandardTestCases.Views):
|
class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = RearPort
|
model = RearPort
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -985,11 +1300,22 @@ class RearPortTestCase(StandardTestCases.Views):
|
|||||||
'name': 'Rear Port X',
|
'name': 'Rear Port X',
|
||||||
'type': PortTypeChoices.TYPE_8P8C,
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
'positions': 3,
|
'positions': 3,
|
||||||
'description': 'New description',
|
'description': 'A rear port',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'cable': None,
|
'device': device.pk,
|
||||||
|
'name_pattern': 'Rear Port [4-6]',
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'positions': 3,
|
||||||
|
'description': 'A rear port',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'type': PortTypeChoices.TYPE_8P8C,
|
||||||
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -1000,16 +1326,11 @@ class RearPortTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTestCase(StandardTestCases.Views):
|
class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
|
|
||||||
# Disable inapplicable views
|
# Disable inapplicable views
|
||||||
test_get_object = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
test_bulk_edit_objects = None
|
test_bulk_edit_objects = None
|
||||||
test_bulk_delete_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
@ -1030,9 +1351,13 @@ class DeviceBayTestCase(StandardTestCases.Views):
|
|||||||
'name': 'Device Bay X',
|
'name': 'Device Bay X',
|
||||||
'description': 'A device bay',
|
'description': 'A device bay',
|
||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
# Extraneous model fields
|
cls.bulk_create_data = {
|
||||||
'installed_device': None,
|
'device': device2.pk,
|
||||||
|
'name_pattern': 'Device Bay [4-6]',
|
||||||
|
'description': 'A device bay',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
@ -1043,15 +1368,9 @@ class DeviceBayTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemTestCase(StandardTestCases.Views):
|
class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
|
|
||||||
# Disable inapplicable views
|
|
||||||
test_get_object = None
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
test_create_object = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
device = create_test_device('Device 1')
|
device = create_test_device('Device 1')
|
||||||
@ -1076,12 +1395,17 @@ class InventoryItemTestCase(StandardTestCases.Views):
|
|||||||
'tags': 'Alpha,Bravo,Charlie',
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.bulk_create_data = {
|
||||||
"device,name",
|
'device': device.pk,
|
||||||
"Device 1,Inventory Item 4",
|
'name_pattern': 'Inventory Item [4-6]',
|
||||||
"Device 1,Inventory Item 5",
|
'manufacturer': manufacturer.pk,
|
||||||
"Device 1,Inventory Item 6",
|
'parent': None,
|
||||||
)
|
'discovered': False,
|
||||||
|
'part_id': '123456',
|
||||||
|
'serial': '123ABC',
|
||||||
|
'description': 'An inventory item',
|
||||||
|
'tags': 'Alpha,Bravo,Charlie',
|
||||||
|
}
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
@ -1090,8 +1414,15 @@ class InventoryItemTestCase(StandardTestCases.Views):
|
|||||||
'description': 'New description',
|
'description': 'New description',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cls.csv_data = (
|
||||||
|
"device,name",
|
||||||
|
"Device 1,Inventory Item 4",
|
||||||
|
"Device 1,Inventory Item 5",
|
||||||
|
"Device 1,Inventory Item 6",
|
||||||
|
)
|
||||||
|
|
||||||
class CableTestCase(StandardTestCases.Views):
|
|
||||||
|
class CableTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Cable
|
model = Cable
|
||||||
|
|
||||||
# TODO: Creation URL needs termination context
|
# TODO: Creation URL needs termination context
|
||||||
@ -1165,7 +1496,7 @@ class CableTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VirtualChassisTestCase(StandardTestCases.Views):
|
class VirtualChassisTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = VirtualChassis
|
model = VirtualChassis
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
@ -1219,7 +1550,7 @@ class VirtualChassisTestCase(StandardTestCases.Views):
|
|||||||
Device.objects.filter(pk=device6.pk).update(virtual_chassis=vc3, vc_position=2)
|
Device.objects.filter(pk=device6.pk).update(virtual_chassis=vc3, vc_position=2)
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelTestCase(StandardTestCases.Views):
|
class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
@ -1260,7 +1591,7 @@ class PowerPanelTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedTestCase(StandardTestCases.Views):
|
class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = PowerFeed
|
model = PowerFeed
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -14,317 +14,338 @@ app_name = 'dcim'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
# Regions
|
# Regions
|
||||||
path(r'regions/', views.RegionListView.as_view(), name='region_list'),
|
path('regions/', views.RegionListView.as_view(), name='region_list'),
|
||||||
path(r'regions/add/', views.RegionCreateView.as_view(), name='region_add'),
|
path('regions/add/', views.RegionCreateView.as_view(), name='region_add'),
|
||||||
path(r'regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
|
path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
|
||||||
path(r'regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
|
path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
|
||||||
path(r'regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
|
path('regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
|
||||||
path(r'regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
path('regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
||||||
|
|
||||||
# Sites
|
# Sites
|
||||||
path(r'sites/', views.SiteListView.as_view(), name='site_list'),
|
path('sites/', views.SiteListView.as_view(), name='site_list'),
|
||||||
path(r'sites/add/', views.SiteCreateView.as_view(), name='site_add'),
|
path('sites/add/', views.SiteCreateView.as_view(), name='site_add'),
|
||||||
path(r'sites/import/', views.SiteBulkImportView.as_view(), name='site_import'),
|
path('sites/import/', views.SiteBulkImportView.as_view(), name='site_import'),
|
||||||
path(r'sites/edit/', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
|
path('sites/edit/', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
|
||||||
path(r'sites/delete/', views.SiteBulkDeleteView.as_view(), name='site_bulk_delete'),
|
path('sites/delete/', views.SiteBulkDeleteView.as_view(), name='site_bulk_delete'),
|
||||||
path(r'sites/<slug:slug>/', views.SiteView.as_view(), name='site'),
|
path('sites/<slug:slug>/', views.SiteView.as_view(), name='site'),
|
||||||
path(r'sites/<slug:slug>/edit/', views.SiteEditView.as_view(), name='site_edit'),
|
path('sites/<slug:slug>/edit/', views.SiteEditView.as_view(), name='site_edit'),
|
||||||
path(r'sites/<slug:slug>/delete/', views.SiteDeleteView.as_view(), name='site_delete'),
|
path('sites/<slug:slug>/delete/', views.SiteDeleteView.as_view(), name='site_delete'),
|
||||||
path(r'sites/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}),
|
path('sites/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}),
|
||||||
path(r'sites/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}),
|
path('sites/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}),
|
||||||
|
|
||||||
# Rack groups
|
# Rack groups
|
||||||
path(r'rack-groups/', views.RackGroupListView.as_view(), name='rackgroup_list'),
|
path('rack-groups/', views.RackGroupListView.as_view(), name='rackgroup_list'),
|
||||||
path(r'rack-groups/add/', views.RackGroupCreateView.as_view(), name='rackgroup_add'),
|
path('rack-groups/add/', views.RackGroupCreateView.as_view(), name='rackgroup_add'),
|
||||||
path(r'rack-groups/import/', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'),
|
path('rack-groups/import/', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'),
|
||||||
path(r'rack-groups/delete/', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
|
path('rack-groups/delete/', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
|
||||||
path(r'rack-groups/<int:pk>/edit/', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
|
path('rack-groups/<int:pk>/edit/', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
|
||||||
path(r'rack-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackgroup_changelog', kwargs={'model': RackGroup}),
|
path('rack-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackgroup_changelog', kwargs={'model': RackGroup}),
|
||||||
|
|
||||||
# Rack roles
|
# Rack roles
|
||||||
path(r'rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'),
|
path('rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'),
|
||||||
path(r'rack-roles/add/', views.RackRoleCreateView.as_view(), name='rackrole_add'),
|
path('rack-roles/add/', views.RackRoleCreateView.as_view(), name='rackrole_add'),
|
||||||
path(r'rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
|
path('rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
|
||||||
path(r'rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
|
path('rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
|
||||||
path(r'rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
path('rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
||||||
path(r'rack-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
|
path('rack-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
|
||||||
|
|
||||||
# Rack reservations
|
# Rack reservations
|
||||||
path(r'rack-reservations/', views.RackReservationListView.as_view(), name='rackreservation_list'),
|
path('rack-reservations/', views.RackReservationListView.as_view(), name='rackreservation_list'),
|
||||||
path(r'rack-reservations/edit/', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
|
path('rack-reservations/edit/', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
|
||||||
path(r'rack-reservations/delete/', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
|
path('rack-reservations/delete/', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
|
||||||
path(r'rack-reservations/<int:pk>/edit/', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
|
path('rack-reservations/<int:pk>/edit/', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
|
||||||
path(r'rack-reservations/<int:pk>/delete/', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
|
path('rack-reservations/<int:pk>/delete/', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
|
||||||
path(r'rack-reservations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackreservation_changelog', kwargs={'model': RackReservation}),
|
path('rack-reservations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackreservation_changelog', kwargs={'model': RackReservation}),
|
||||||
|
|
||||||
# Racks
|
# Racks
|
||||||
path(r'racks/', views.RackListView.as_view(), name='rack_list'),
|
path('racks/', views.RackListView.as_view(), name='rack_list'),
|
||||||
path(r'rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'),
|
path('rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'),
|
||||||
path(r'racks/add/', views.RackCreateView.as_view(), name='rack_add'),
|
path('racks/add/', views.RackCreateView.as_view(), name='rack_add'),
|
||||||
path(r'racks/import/', views.RackBulkImportView.as_view(), name='rack_import'),
|
path('racks/import/', views.RackBulkImportView.as_view(), name='rack_import'),
|
||||||
path(r'racks/edit/', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
|
path('racks/edit/', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
|
||||||
path(r'racks/delete/', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
|
path('racks/delete/', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
|
||||||
path(r'racks/<int:pk>/', views.RackView.as_view(), name='rack'),
|
path('racks/<int:pk>/', views.RackView.as_view(), name='rack'),
|
||||||
path(r'racks/<int:pk>/edit/', views.RackEditView.as_view(), name='rack_edit'),
|
path('racks/<int:pk>/edit/', views.RackEditView.as_view(), name='rack_edit'),
|
||||||
path(r'racks/<int:pk>/delete/', views.RackDeleteView.as_view(), name='rack_delete'),
|
path('racks/<int:pk>/delete/', views.RackDeleteView.as_view(), name='rack_delete'),
|
||||||
path(r'racks/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}),
|
path('racks/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}),
|
||||||
path(r'racks/<int:rack>/reservations/add/', views.RackReservationCreateView.as_view(), name='rack_add_reservation'),
|
path('racks/<int:rack>/reservations/add/', views.RackReservationCreateView.as_view(), name='rack_add_reservation'),
|
||||||
path(r'racks/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}),
|
path('racks/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}),
|
||||||
|
|
||||||
# Manufacturers
|
# Manufacturers
|
||||||
path(r'manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'),
|
path('manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'),
|
||||||
path(r'manufacturers/add/', views.ManufacturerCreateView.as_view(), name='manufacturer_add'),
|
path('manufacturers/add/', views.ManufacturerCreateView.as_view(), name='manufacturer_add'),
|
||||||
path(r'manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
|
path('manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
|
||||||
path(r'manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
|
path('manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
|
||||||
path(r'manufacturers/<slug:slug>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
|
path('manufacturers/<slug:slug>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
|
||||||
path(r'manufacturers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
|
path('manufacturers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
|
||||||
|
|
||||||
# Device types
|
# Device types
|
||||||
path(r'device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'),
|
path('device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'),
|
||||||
path(r'device-types/add/', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
|
path('device-types/add/', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
|
||||||
path(r'device-types/import/', views.DeviceTypeImportView.as_view(), name='devicetype_import'),
|
path('device-types/import/', views.DeviceTypeImportView.as_view(), name='devicetype_import'),
|
||||||
path(r'device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
|
path('device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
|
||||||
path(r'device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
|
path('device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
|
||||||
path(r'device-types/<int:pk>/', views.DeviceTypeView.as_view(), name='devicetype'),
|
path('device-types/<int:pk>/', views.DeviceTypeView.as_view(), name='devicetype'),
|
||||||
path(r'device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
|
path('device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
|
||||||
path(r'device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
|
path('device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
|
||||||
path(r'device-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='devicetype_changelog', kwargs={'model': DeviceType}),
|
path('device-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='devicetype_changelog', kwargs={'model': DeviceType}),
|
||||||
|
|
||||||
# Console port templates
|
# Console port templates
|
||||||
path(r'device-types/<int:pk>/console-ports/add/', views.ConsolePortTemplateCreateView.as_view(), name='devicetype_add_consoleport'),
|
path('console-port-templates/add/', views.ConsolePortTemplateCreateView.as_view(), name='consoleporttemplate_add'),
|
||||||
path(r'device-types/<int:pk>/console-ports/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleport'),
|
path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'),
|
||||||
path(r'console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
|
path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'),
|
||||||
|
path('console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
|
||||||
|
path('console-port-templates/<int:pk>/delete/', views.ConsolePortTemplateDeleteView.as_view(), name='consoleporttemplate_delete'),
|
||||||
|
|
||||||
# Console server port templates
|
# Console server port templates
|
||||||
path(r'device-types/<int:pk>/console-server-ports/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='devicetype_add_consoleserverport'),
|
path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'),
|
||||||
path(r'device-types/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleserverport'),
|
path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'),
|
||||||
path(r'console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
|
path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'),
|
||||||
|
path('console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
|
||||||
|
path('console-server-port-templates/<int:pk>/delete/', views.ConsoleServerPortTemplateDeleteView.as_view(), name='consoleserverporttemplate_delete'),
|
||||||
|
|
||||||
# Power port templates
|
# Power port templates
|
||||||
path(r'device-types/<int:pk>/power-ports/add/', views.PowerPortTemplateCreateView.as_view(), name='devicetype_add_powerport'),
|
path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'),
|
||||||
path(r'device-types/<int:pk>/power-ports/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_powerport'),
|
path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'),
|
||||||
path(r'power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
|
path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'),
|
||||||
|
path('power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
|
||||||
|
path('power-port-templates/<int:pk>/delete/', views.PowerPortTemplateDeleteView.as_view(), name='powerporttemplate_delete'),
|
||||||
|
|
||||||
# Power outlet templates
|
# Power outlet templates
|
||||||
path(r'device-types/<int:pk>/power-outlets/add/', views.PowerOutletTemplateCreateView.as_view(), name='devicetype_add_poweroutlet'),
|
path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'),
|
||||||
path(r'device-types/<int:pk>/power-outlets/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='devicetype_delete_poweroutlet'),
|
path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'),
|
||||||
path(r'power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
|
path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'),
|
||||||
|
path('power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
|
||||||
|
path('power-outlet-templates/<int:pk>/delete/', views.PowerOutletTemplateDeleteView.as_view(), name='poweroutlettemplate_delete'),
|
||||||
|
|
||||||
# Interface templates
|
# Interface templates
|
||||||
path(r'device-types/<int:pk>/interfaces/add/', views.InterfaceTemplateCreateView.as_view(), name='devicetype_add_interface'),
|
path('interface-templates/add/', views.InterfaceTemplateCreateView.as_view(), name='interfacetemplate_add'),
|
||||||
path(r'device-types/<int:pk>/interfaces/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='devicetype_bulkedit_interface'),
|
path('interface-templates/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='interfacetemplate_bulk_edit'),
|
||||||
path(r'device-types/<int:pk>/interfaces/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='devicetype_delete_interface'),
|
path('interface-templates/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='interfacetemplate_bulk_delete'),
|
||||||
path(r'interface-templates/<int:pk>/edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
|
path('interface-templates/<int:pk>/edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
|
||||||
|
path('interface-templates/<int:pk>/delete/', views.InterfaceTemplateDeleteView.as_view(), name='interfacetemplate_delete'),
|
||||||
|
|
||||||
# Front port templates
|
# Front port templates
|
||||||
path(r'device-types/<int:pk>/front-ports/add/', views.FrontPortTemplateCreateView.as_view(), name='devicetype_add_frontport'),
|
path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'),
|
||||||
path(r'device-types/<int:pk>/front-ports/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_frontport'),
|
path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'),
|
||||||
path(r'front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
|
path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'),
|
||||||
|
path('front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
|
||||||
|
path('front-port-templates/<int:pk>/delete/', views.FrontPortTemplateDeleteView.as_view(), name='frontporttemplate_delete'),
|
||||||
|
|
||||||
# Rear port templates
|
# Rear port templates
|
||||||
path(r'device-types/<int:pk>/rear-ports/add/', views.RearPortTemplateCreateView.as_view(), name='devicetype_add_rearport'),
|
path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'),
|
||||||
path(r'device-types/<int:pk>/rear-ports/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_rearport'),
|
path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'),
|
||||||
path(r'rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
|
path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'),
|
||||||
|
path('rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
|
||||||
|
path('rear-port-templates/<int:pk>/delete/', views.RearPortTemplateDeleteView.as_view(), name='rearporttemplate_delete'),
|
||||||
|
|
||||||
# Device bay templates
|
# Device bay templates
|
||||||
path(r'device-types/<int:pk>/device-bays/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicetype_add_devicebay'),
|
path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'),
|
||||||
path(r'device-types/<int:pk>/device-bays/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicetype_delete_devicebay'),
|
# path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'),
|
||||||
path(r'device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
|
path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'),
|
||||||
|
path('device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
|
||||||
|
path('device-bay-templates/<int:pk>/delete/', views.DeviceBayTemplateDeleteView.as_view(), name='devicebaytemplate_delete'),
|
||||||
|
|
||||||
# Device roles
|
# Device roles
|
||||||
path(r'device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
||||||
path(r'device-roles/add/', views.DeviceRoleCreateView.as_view(), name='devicerole_add'),
|
path('device-roles/add/', views.DeviceRoleCreateView.as_view(), name='devicerole_add'),
|
||||||
path(r'device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
|
path('device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
|
||||||
path(r'device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
|
path('device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
|
||||||
path(r'device-roles/<slug:slug>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
|
path('device-roles/<slug:slug>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
|
||||||
path(r'device-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
|
path('device-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
|
||||||
|
|
||||||
# Platforms
|
# Platforms
|
||||||
path(r'platforms/', views.PlatformListView.as_view(), name='platform_list'),
|
path('platforms/', views.PlatformListView.as_view(), name='platform_list'),
|
||||||
path(r'platforms/add/', views.PlatformCreateView.as_view(), name='platform_add'),
|
path('platforms/add/', views.PlatformCreateView.as_view(), name='platform_add'),
|
||||||
path(r'platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
|
path('platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
|
||||||
path(r'platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
|
path('platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
|
||||||
path(r'platforms/<slug:slug>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
|
path('platforms/<slug:slug>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
|
||||||
path(r'platforms/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
|
path('platforms/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
|
||||||
|
|
||||||
# Devices
|
# Devices
|
||||||
path(r'devices/', views.DeviceListView.as_view(), name='device_list'),
|
path('devices/', views.DeviceListView.as_view(), name='device_list'),
|
||||||
path(r'devices/add/', views.DeviceCreateView.as_view(), name='device_add'),
|
path('devices/add/', views.DeviceCreateView.as_view(), name='device_add'),
|
||||||
path(r'devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
|
path('devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
|
||||||
path(r'devices/import/child-devices/', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
|
path('devices/import/child-devices/', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
|
||||||
path(r'devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
|
path('devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
|
||||||
path(r'devices/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
|
path('devices/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
|
||||||
path(r'devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
|
path('devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
|
||||||
path(r'devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
|
path('devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
|
||||||
path(r'devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
|
path('devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
|
||||||
path(r'devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
|
path('devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
|
||||||
path(r'devices/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
|
path('devices/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
|
||||||
path(r'devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
|
path('devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
|
||||||
path(r'devices/<int:pk>/status/', views.DeviceStatusView.as_view(), name='device_status'),
|
path('devices/<int:pk>/status/', views.DeviceStatusView.as_view(), name='device_status'),
|
||||||
path(r'devices/<int:pk>/lldp-neighbors/', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'),
|
path('devices/<int:pk>/lldp-neighbors/', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'),
|
||||||
path(r'devices/<int:pk>/config/', views.DeviceConfigView.as_view(), name='device_config'),
|
path('devices/<int:pk>/config/', views.DeviceConfigView.as_view(), name='device_config'),
|
||||||
path(r'devices/<int:pk>/add-secret/', secret_add, name='device_addsecret'),
|
path('devices/<int:pk>/add-secret/', secret_add, name='device_addsecret'),
|
||||||
path(r'devices/<int:device>/services/assign/', ServiceCreateView.as_view(), name='device_service_assign'),
|
path('devices/<int:device>/services/assign/', ServiceCreateView.as_view(), name='device_service_assign'),
|
||||||
path(r'devices/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}),
|
path('devices/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}),
|
||||||
|
|
||||||
# Console ports
|
# Console ports
|
||||||
path(r'devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
path('console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'),
|
||||||
path(r'devices/<int:pk>/console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
path('console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
||||||
path(r'devices/<int:pk>/console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
path('console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'),
|
||||||
path(r'console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'),
|
path('console-ports/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'),
|
||||||
path(r'console-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
|
# TODO: Bulk rename, disconnect views for ConsolePorts
|
||||||
path(r'console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||||
path(r'console-ports/<int:pk>/delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
path('console-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
|
||||||
path(r'console-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
|
path('console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
||||||
path(r'console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'),
|
path('console-ports/<int:pk>/delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
||||||
|
path('console-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
|
||||||
|
path('devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
||||||
|
|
||||||
# Console server ports
|
# Console server ports
|
||||||
path(r'devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
|
path('console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'),
|
||||||
path(r'devices/<int:pk>/console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
path('console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
|
||||||
path(r'devices/<int:pk>/console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
|
path('console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'),
|
||||||
path(r'devices/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
path('console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
|
||||||
path(r'console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'),
|
path('console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
|
||||||
path(r'console-server-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
|
path('console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
||||||
path(r'console-server-ports/<int:pk>/edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
path('console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
||||||
path(r'console-server-ports/<int:pk>/delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
path('console-server-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
|
||||||
path(r'console-server-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
|
path('console-server-ports/<int:pk>/edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
|
||||||
path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
|
path('console-server-ports/<int:pk>/delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
||||||
path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
|
path('console-server-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
|
||||||
path(r'console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'),
|
path('devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
|
||||||
|
|
||||||
# Power ports
|
# Power ports
|
||||||
path(r'devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
path('power-ports/', views.PowerPortListView.as_view(), name='powerport_list'),
|
||||||
path(r'devices/<int:pk>/power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
path('power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
||||||
path(r'devices/<int:pk>/power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
path('power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'),
|
||||||
path(r'power-ports/', views.PowerPortListView.as_view(), name='powerport_list'),
|
path('power-ports/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'),
|
||||||
path(r'power-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
|
# TODO: Bulk rename, disconnect views for PowerPorts
|
||||||
path(r'power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||||
path(r'power-ports/<int:pk>/delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
path('power-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
|
||||||
path(r'power-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
|
path('power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
||||||
path(r'power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'),
|
path('power-ports/<int:pk>/delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
||||||
|
path('power-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
|
||||||
|
path('devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
||||||
|
|
||||||
# Power outlets
|
# Power outlets
|
||||||
path(r'devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
|
path('power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'),
|
||||||
path(r'devices/<int:pk>/power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
path('power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
|
||||||
path(r'devices/<int:pk>/power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
|
path('power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'),
|
||||||
path(r'devices/<int:pk>/power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
path('power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
|
||||||
path(r'power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'),
|
path('power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
|
||||||
path(r'power-outlets/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
|
path('power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
||||||
path(r'power-outlets/<int:pk>/edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
path('power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
||||||
path(r'power-outlets/<int:pk>/delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
path('power-outlets/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
|
||||||
path(r'power-outlets/<int:pk>/trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
|
path('power-outlets/<int:pk>/edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
||||||
path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
|
path('power-outlets/<int:pk>/delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
||||||
path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
|
path('power-outlets/<int:pk>/trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
|
||||||
path(r'power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'),
|
path('devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
|
||||||
|
|
||||||
# Interfaces
|
# Interfaces
|
||||||
path(r'devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
|
path('interfaces/', views.InterfaceListView.as_view(), name='interface_list'),
|
||||||
path(r'devices/<int:pk>/interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
|
path('interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
|
||||||
path(r'devices/<int:pk>/interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
path('interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'),
|
||||||
path(r'devices/<int:pk>/interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
path('interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||||
path(r'interfaces/', views.InterfaceListView.as_view(), name='interface_list'),
|
path('interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
|
||||||
path(r'interfaces/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
|
path('interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
|
||||||
path(r'interfaces/<int:pk>/', views.InterfaceView.as_view(), name='interface'),
|
path('interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||||
path(r'interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
|
path('interfaces/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
|
||||||
path(r'interfaces/<int:pk>/delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
path('interfaces/<int:pk>/', views.InterfaceView.as_view(), name='interface'),
|
||||||
path(r'interfaces/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
|
path('interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||||
path(r'interfaces/<int:pk>/trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
|
path('interfaces/<int:pk>/delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||||
path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
|
path('interfaces/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
|
||||||
path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
|
path('interfaces/<int:pk>/trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
|
||||||
path(r'interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'),
|
path('devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
|
||||||
|
|
||||||
# Front ports
|
# Front ports
|
||||||
# path(r'devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
|
path('front-ports/', views.FrontPortListView.as_view(), name='frontport_list'),
|
||||||
path(r'devices/<int:pk>/front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'),
|
path('front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'),
|
||||||
path(r'devices/<int:pk>/front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
|
path('front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'),
|
||||||
path(r'devices/<int:pk>/front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
|
path('front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
|
||||||
path(r'front-ports/', views.FrontPortListView.as_view(), name='frontport_list'),
|
path('front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
|
||||||
path(r'front-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
|
path('front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
|
||||||
path(r'front-ports/<int:pk>/edit/', views.FrontPortEditView.as_view(), name='frontport_edit'),
|
path('front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
|
||||||
path(r'front-ports/<int:pk>/delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
|
path('front-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
|
||||||
path(r'front-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
|
path('front-ports/<int:pk>/edit/', views.FrontPortEditView.as_view(), name='frontport_edit'),
|
||||||
path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
|
path('front-ports/<int:pk>/delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
|
||||||
path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
|
path('front-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
|
||||||
path(r'front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'),
|
# path('devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
|
||||||
|
|
||||||
# Rear ports
|
# Rear ports
|
||||||
# path(r'devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
path('rear-ports/', views.RearPortListView.as_view(), name='rearport_list'),
|
||||||
path(r'devices/<int:pk>/rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'),
|
path('rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'),
|
||||||
path(r'devices/<int:pk>/rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
|
path('rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'),
|
||||||
path(r'devices/<int:pk>/rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
|
path('rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
|
||||||
path(r'rear-ports/', views.RearPortListView.as_view(), name='rearport_list'),
|
path('rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
|
||||||
path(r'rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
path('rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
|
||||||
path(r'rear-ports/<int:pk>/edit/', views.RearPortEditView.as_view(), name='rearport_edit'),
|
path('rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
|
||||||
path(r'rear-ports/<int:pk>/delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'),
|
path('rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
||||||
path(r'rear-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
|
path('rear-ports/<int:pk>/edit/', views.RearPortEditView.as_view(), name='rearport_edit'),
|
||||||
path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
|
path('rear-ports/<int:pk>/delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'),
|
||||||
path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
|
path('rear-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
|
||||||
path(r'rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'),
|
# path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
||||||
|
|
||||||
# Device bays
|
# Device bays
|
||||||
path(r'devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
path('device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'),
|
||||||
path(r'devices/<int:pk>/bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
path('device-bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
||||||
path(r'devices/<int:pk>/bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
path('device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'),
|
||||||
path(r'device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'),
|
# TODO: Bulk edit view for DeviceBays
|
||||||
path(r'device-bays/<int:pk>/edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
path('device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
|
||||||
path(r'device-bays/<int:pk>/delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
|
path('device-bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
||||||
path(r'device-bays/<int:pk>/populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
|
path('device-bays/<int:pk>/edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
||||||
path(r'device-bays/<int:pk>/depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
|
path('device-bays/<int:pk>/delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
|
||||||
path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
|
path('device-bays/<int:pk>/populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
|
||||||
path(r'device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'),
|
path('device-bays/<int:pk>/depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
|
||||||
|
path('devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
||||||
|
|
||||||
# Inventory items
|
# Inventory items
|
||||||
path(r'inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
|
path('inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
|
||||||
path(r'inventory-items/import/', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
|
path('inventory-items/add/', views.InventoryItemCreateView.as_view(), name='inventoryitem_add'),
|
||||||
path(r'inventory-items/edit/', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
|
path('inventory-items/import/', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
|
||||||
path(r'inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
|
path('inventory-items/edit/', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
|
||||||
path(r'inventory-items/<int:pk>/edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
|
# TODO: Bulk rename view for InventoryItems
|
||||||
path(r'inventory-items/<int:pk>/delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
|
path('inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
|
||||||
path(r'devices/<int:device>/inventory-items/add/', views.InventoryItemEditView.as_view(), name='inventoryitem_add'),
|
path('inventory-items/<int:pk>/edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
|
||||||
|
path('inventory-items/<int:pk>/delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
|
||||||
|
|
||||||
# Cables
|
# Cables
|
||||||
path(r'cables/', views.CableListView.as_view(), name='cable_list'),
|
path('cables/', views.CableListView.as_view(), name='cable_list'),
|
||||||
path(r'cables/import/', views.CableBulkImportView.as_view(), name='cable_import'),
|
path('cables/import/', views.CableBulkImportView.as_view(), name='cable_import'),
|
||||||
path(r'cables/edit/', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
|
path('cables/edit/', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
|
||||||
path(r'cables/delete/', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
|
path('cables/delete/', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
|
||||||
path(r'cables/<int:pk>/', views.CableView.as_view(), name='cable'),
|
path('cables/<int:pk>/', views.CableView.as_view(), name='cable'),
|
||||||
path(r'cables/<int:pk>/edit/', views.CableEditView.as_view(), name='cable_edit'),
|
path('cables/<int:pk>/edit/', views.CableEditView.as_view(), name='cable_edit'),
|
||||||
path(r'cables/<int:pk>/delete/', views.CableDeleteView.as_view(), name='cable_delete'),
|
path('cables/<int:pk>/delete/', views.CableDeleteView.as_view(), name='cable_delete'),
|
||||||
path(r'cables/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cable_changelog', kwargs={'model': Cable}),
|
path('cables/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cable_changelog', kwargs={'model': Cable}),
|
||||||
|
|
||||||
# Console/power/interface connections (read-only)
|
# Console/power/interface connections (read-only)
|
||||||
path(r'console-connections/', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
|
path('console-connections/', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
|
||||||
path(r'power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
|
path('power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
|
||||||
path(r'interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
|
path('interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
|
||||||
|
|
||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
path(r'virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
|
path('virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
|
||||||
path(r'virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
|
path('virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
|
||||||
path(r'virtual-chassis/<int:pk>/edit/', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
|
path('virtual-chassis/<int:pk>/edit/', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
|
||||||
path(r'virtual-chassis/<int:pk>/delete/', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
|
path('virtual-chassis/<int:pk>/delete/', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
|
||||||
path(r'virtual-chassis/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}),
|
path('virtual-chassis/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}),
|
||||||
path(r'virtual-chassis/<int:pk>/add-member/', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
|
path('virtual-chassis/<int:pk>/add-member/', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
|
||||||
path(r'virtual-chassis-members/<int:pk>/delete/', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
|
path('virtual-chassis-members/<int:pk>/delete/', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
|
||||||
|
|
||||||
# Power panels
|
# Power panels
|
||||||
path(r'power-panels/', views.PowerPanelListView.as_view(), name='powerpanel_list'),
|
path('power-panels/', views.PowerPanelListView.as_view(), name='powerpanel_list'),
|
||||||
path(r'power-panels/add/', views.PowerPanelCreateView.as_view(), name='powerpanel_add'),
|
path('power-panels/add/', views.PowerPanelCreateView.as_view(), name='powerpanel_add'),
|
||||||
path(r'power-panels/import/', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
|
path('power-panels/import/', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
|
||||||
path(r'power-panels/delete/', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
|
path('power-panels/delete/', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
|
||||||
path(r'power-panels/<int:pk>/', views.PowerPanelView.as_view(), name='powerpanel'),
|
path('power-panels/<int:pk>/', views.PowerPanelView.as_view(), name='powerpanel'),
|
||||||
path(r'power-panels/<int:pk>/edit/', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
|
path('power-panels/<int:pk>/edit/', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
|
||||||
path(r'power-panels/<int:pk>/delete/', views.PowerPanelDeleteView.as_view(), name='powerpanel_delete'),
|
path('power-panels/<int:pk>/delete/', views.PowerPanelDeleteView.as_view(), name='powerpanel_delete'),
|
||||||
path(r'power-panels/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerpanel_changelog', kwargs={'model': PowerPanel}),
|
path('power-panels/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerpanel_changelog', kwargs={'model': PowerPanel}),
|
||||||
|
|
||||||
# Power feeds
|
# Power feeds
|
||||||
path(r'power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'),
|
path('power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'),
|
||||||
path(r'power-feeds/add/', views.PowerFeedCreateView.as_view(), name='powerfeed_add'),
|
path('power-feeds/add/', views.PowerFeedCreateView.as_view(), name='powerfeed_add'),
|
||||||
path(r'power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
|
path('power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
|
||||||
path(r'power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
|
path('power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
|
||||||
path(r'power-feeds/delete/', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
|
path('power-feeds/delete/', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
|
||||||
path(r'power-feeds/<int:pk>/', views.PowerFeedView.as_view(), name='powerfeed'),
|
path('power-feeds/<int:pk>/', views.PowerFeedView.as_view(), name='powerfeed'),
|
||||||
path(r'power-feeds/<int:pk>/edit/', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
|
path('power-feeds/<int:pk>/edit/', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
|
||||||
path(r'power-feeds/<int:pk>/delete/', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
|
path('power-feeds/<int:pk>/delete/', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
|
||||||
path(r'power-feeds/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerfeed_changelog', kwargs={'model': PowerFeed}),
|
path('power-feeds/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerfeed_changelog', kwargs={'model': PowerFeed}),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -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):
|
||||||
@ -700,13 +693,11 @@ class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
|||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device type components
|
# Console port templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsolePortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class ConsolePortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_consoleporttemplate'
|
permission_required = 'dcim.add_consoleporttemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = ConsolePortTemplate
|
model = ConsolePortTemplate
|
||||||
form = forms.ConsolePortTemplateCreateForm
|
form = forms.ConsolePortTemplateCreateForm
|
||||||
model_form = forms.ConsolePortTemplateForm
|
model_form = forms.ConsolePortTemplateForm
|
||||||
@ -719,17 +710,30 @@ class ConsolePortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.ConsolePortTemplateForm
|
model_form = forms.ConsolePortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class ConsolePortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_consoleporttemplate'
|
||||||
|
model = ConsolePortTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class ConsolePortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_consoleporttemplate'
|
||||||
|
queryset = ConsolePortTemplate.objects.all()
|
||||||
|
table = tables.ConsolePortTemplateTable
|
||||||
|
form = forms.ConsolePortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class ConsolePortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_consoleporttemplate'
|
permission_required = 'dcim.delete_consoleporttemplate'
|
||||||
queryset = ConsolePortTemplate.objects.all()
|
queryset = ConsolePortTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.ConsolePortTemplateTable
|
table = tables.ConsolePortTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Console server port templates
|
||||||
|
#
|
||||||
|
|
||||||
class ConsoleServerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class ConsoleServerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_consoleserverporttemplate'
|
permission_required = 'dcim.add_consoleserverporttemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = ConsoleServerPortTemplate
|
model = ConsoleServerPortTemplate
|
||||||
form = forms.ConsoleServerPortTemplateCreateForm
|
form = forms.ConsoleServerPortTemplateCreateForm
|
||||||
model_form = forms.ConsoleServerPortTemplateForm
|
model_form = forms.ConsoleServerPortTemplateForm
|
||||||
@ -742,17 +746,30 @@ class ConsoleServerPortTemplateEditView(PermissionRequiredMixin, ObjectEditView)
|
|||||||
model_form = forms.ConsoleServerPortTemplateForm
|
model_form = forms.ConsoleServerPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class ConsoleServerPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_consoleserverporttemplate'
|
||||||
|
model = ConsoleServerPortTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class ConsoleServerPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_consoleserverporttemplate'
|
||||||
|
queryset = ConsoleServerPortTemplate.objects.all()
|
||||||
|
table = tables.ConsoleServerPortTemplateTable
|
||||||
|
form = forms.ConsoleServerPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class ConsoleServerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_consoleserverporttemplate'
|
permission_required = 'dcim.delete_consoleserverporttemplate'
|
||||||
queryset = ConsoleServerPortTemplate.objects.all()
|
queryset = ConsoleServerPortTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.ConsoleServerPortTemplateTable
|
table = tables.ConsoleServerPortTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Power port templates
|
||||||
|
#
|
||||||
|
|
||||||
class PowerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class PowerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_powerporttemplate'
|
permission_required = 'dcim.add_powerporttemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = PowerPortTemplate
|
model = PowerPortTemplate
|
||||||
form = forms.PowerPortTemplateCreateForm
|
form = forms.PowerPortTemplateCreateForm
|
||||||
model_form = forms.PowerPortTemplateForm
|
model_form = forms.PowerPortTemplateForm
|
||||||
@ -765,17 +782,30 @@ class PowerPortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.PowerPortTemplateForm
|
model_form = forms.PowerPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class PowerPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_powerporttemplate'
|
||||||
|
model = PowerPortTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class PowerPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_powerporttemplate'
|
||||||
|
queryset = PowerPortTemplate.objects.all()
|
||||||
|
table = tables.PowerPortTemplateTable
|
||||||
|
form = forms.PowerPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class PowerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_powerporttemplate'
|
permission_required = 'dcim.delete_powerporttemplate'
|
||||||
queryset = PowerPortTemplate.objects.all()
|
queryset = PowerPortTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.PowerPortTemplateTable
|
table = tables.PowerPortTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Power outlet templates
|
||||||
|
#
|
||||||
|
|
||||||
class PowerOutletTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class PowerOutletTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_poweroutlettemplate'
|
permission_required = 'dcim.add_poweroutlettemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = PowerOutletTemplate
|
model = PowerOutletTemplate
|
||||||
form = forms.PowerOutletTemplateCreateForm
|
form = forms.PowerOutletTemplateCreateForm
|
||||||
model_form = forms.PowerOutletTemplateForm
|
model_form = forms.PowerOutletTemplateForm
|
||||||
@ -788,17 +818,30 @@ class PowerOutletTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.PowerOutletTemplateForm
|
model_form = forms.PowerOutletTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class PowerOutletTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_poweroutlettemplate'
|
||||||
|
model = PowerOutletTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class PowerOutletTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_poweroutlettemplate'
|
||||||
|
queryset = PowerOutletTemplate.objects.all()
|
||||||
|
table = tables.PowerOutletTemplateTable
|
||||||
|
form = forms.PowerOutletTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class PowerOutletTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_poweroutlettemplate'
|
permission_required = 'dcim.delete_poweroutlettemplate'
|
||||||
queryset = PowerOutletTemplate.objects.all()
|
queryset = PowerOutletTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.PowerOutletTemplateTable
|
table = tables.PowerOutletTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Interface templates
|
||||||
|
#
|
||||||
|
|
||||||
class InterfaceTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class InterfaceTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_interfacetemplate'
|
permission_required = 'dcim.add_interfacetemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = InterfaceTemplate
|
model = InterfaceTemplate
|
||||||
form = forms.InterfaceTemplateCreateForm
|
form = forms.InterfaceTemplateCreateForm
|
||||||
model_form = forms.InterfaceTemplateForm
|
model_form = forms.InterfaceTemplateForm
|
||||||
@ -811,10 +854,14 @@ class InterfaceTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.InterfaceTemplateForm
|
model_form = forms.InterfaceTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_interfacetemplate'
|
||||||
|
model = InterfaceTemplate
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class InterfaceTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
permission_required = 'dcim.change_interfacetemplate'
|
permission_required = 'dcim.change_interfacetemplate'
|
||||||
queryset = InterfaceTemplate.objects.all()
|
queryset = InterfaceTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.InterfaceTemplateTable
|
table = tables.InterfaceTemplateTable
|
||||||
form = forms.InterfaceTemplateBulkEditForm
|
form = forms.InterfaceTemplateBulkEditForm
|
||||||
|
|
||||||
@ -822,14 +869,15 @@ class InterfaceTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|||||||
class InterfaceTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class InterfaceTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_interfacetemplate'
|
permission_required = 'dcim.delete_interfacetemplate'
|
||||||
queryset = InterfaceTemplate.objects.all()
|
queryset = InterfaceTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.InterfaceTemplateTable
|
table = tables.InterfaceTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Front port templates
|
||||||
|
#
|
||||||
|
|
||||||
class FrontPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class FrontPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_frontporttemplate'
|
permission_required = 'dcim.add_frontporttemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
form = forms.FrontPortTemplateCreateForm
|
form = forms.FrontPortTemplateCreateForm
|
||||||
model_form = forms.FrontPortTemplateForm
|
model_form = forms.FrontPortTemplateForm
|
||||||
@ -842,17 +890,30 @@ class FrontPortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.FrontPortTemplateForm
|
model_form = forms.FrontPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_frontporttemplate'
|
||||||
|
model = FrontPortTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_frontporttemplate'
|
||||||
|
queryset = FrontPortTemplate.objects.all()
|
||||||
|
table = tables.FrontPortTemplateTable
|
||||||
|
form = forms.FrontPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class FrontPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_frontporttemplate'
|
permission_required = 'dcim.delete_frontporttemplate'
|
||||||
queryset = FrontPortTemplate.objects.all()
|
queryset = FrontPortTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.FrontPortTemplateTable
|
table = tables.FrontPortTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rear port templates
|
||||||
|
#
|
||||||
|
|
||||||
class RearPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class RearPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_rearporttemplate'
|
permission_required = 'dcim.add_rearporttemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = RearPortTemplate
|
model = RearPortTemplate
|
||||||
form = forms.RearPortTemplateCreateForm
|
form = forms.RearPortTemplateCreateForm
|
||||||
model_form = forms.RearPortTemplateForm
|
model_form = forms.RearPortTemplateForm
|
||||||
@ -865,17 +926,30 @@ class RearPortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.RearPortTemplateForm
|
model_form = forms.RearPortTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class RearPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_rearporttemplate'
|
||||||
|
model = RearPortTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class RearPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_rearporttemplate'
|
||||||
|
queryset = RearPortTemplate.objects.all()
|
||||||
|
table = tables.RearPortTemplateTable
|
||||||
|
form = forms.RearPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class RearPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_rearporttemplate'
|
permission_required = 'dcim.delete_rearporttemplate'
|
||||||
queryset = RearPortTemplate.objects.all()
|
queryset = RearPortTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.RearPortTemplateTable
|
table = tables.RearPortTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Device bay templates
|
||||||
|
#
|
||||||
|
|
||||||
class DeviceBayTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class DeviceBayTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_devicebaytemplate'
|
permission_required = 'dcim.add_devicebaytemplate'
|
||||||
parent_model = DeviceType
|
|
||||||
parent_field = 'device_type'
|
|
||||||
model = DeviceBayTemplate
|
model = DeviceBayTemplate
|
||||||
form = forms.DeviceBayTemplateCreateForm
|
form = forms.DeviceBayTemplateCreateForm
|
||||||
model_form = forms.DeviceBayTemplateForm
|
model_form = forms.DeviceBayTemplateForm
|
||||||
@ -888,10 +962,21 @@ class DeviceBayTemplateEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model_form = forms.DeviceBayTemplateForm
|
model_form = forms.DeviceBayTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBayTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
|
permission_required = 'dcim.delete_devicebaytemplate'
|
||||||
|
model = DeviceBayTemplate
|
||||||
|
|
||||||
|
|
||||||
|
# class DeviceBayTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
# permission_required = 'dcim.change_devicebaytemplate'
|
||||||
|
# queryset = DeviceBayTemplate.objects.all()
|
||||||
|
# table = tables.DeviceBayTemplateTable
|
||||||
|
# form = forms.DeviceBayTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_devicebaytemplate'
|
permission_required = 'dcim.delete_devicebaytemplate'
|
||||||
queryset = DeviceBayTemplate.objects.all()
|
queryset = DeviceBayTemplate.objects.all()
|
||||||
parent_model = DeviceType
|
|
||||||
table = tables.DeviceBayTemplateTable
|
table = tables.DeviceBayTemplateTable
|
||||||
|
|
||||||
|
|
||||||
@ -903,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):
|
||||||
@ -939,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):
|
||||||
@ -1200,13 +1283,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class ConsolePortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_consoleport'
|
permission_required = 'dcim.add_consoleport'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
form = forms.ConsolePortCreateForm
|
form = forms.ConsolePortCreateForm
|
||||||
model_form = forms.ConsolePortForm
|
model_form = forms.ConsolePortForm
|
||||||
@ -1231,11 +1312,18 @@ class ConsolePortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
default_return_url = 'dcim:consoleport_list'
|
default_return_url = 'dcim:consoleport_list'
|
||||||
|
|
||||||
|
|
||||||
|
class ConsolePortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_consoleport'
|
||||||
|
queryset = ConsolePort.objects.all()
|
||||||
|
table = tables.ConsolePortTable
|
||||||
|
form = forms.ConsolePortBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_consoleport'
|
permission_required = 'dcim.delete_consoleport'
|
||||||
queryset = ConsolePort.objects.all()
|
queryset = ConsolePort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.ConsolePortTable
|
table = tables.ConsolePortTable
|
||||||
|
default_return_url = 'dcim:consoleport_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1248,13 +1336,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class ConsoleServerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_consoleserverport'
|
permission_required = 'dcim.add_consoleserverport'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
form = forms.ConsoleServerPortCreateForm
|
form = forms.ConsoleServerPortCreateForm
|
||||||
model_form = forms.ConsoleServerPortForm
|
model_form = forms.ConsoleServerPortForm
|
||||||
@ -1282,7 +1368,6 @@ class ConsoleServerPortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
class ConsoleServerPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class ConsoleServerPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
permission_required = 'dcim.change_consoleserverport'
|
permission_required = 'dcim.change_consoleserverport'
|
||||||
queryset = ConsoleServerPort.objects.all()
|
queryset = ConsoleServerPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.ConsoleServerPortTable
|
table = tables.ConsoleServerPortTable
|
||||||
form = forms.ConsoleServerPortBulkEditForm
|
form = forms.ConsoleServerPortBulkEditForm
|
||||||
|
|
||||||
@ -1302,8 +1387,8 @@ class ConsoleServerPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnec
|
|||||||
class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_consoleserverport'
|
permission_required = 'dcim.delete_consoleserverport'
|
||||||
queryset = ConsoleServerPort.objects.all()
|
queryset = ConsoleServerPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.ConsoleServerPortTable
|
table = tables.ConsoleServerPortTable
|
||||||
|
default_return_url = 'dcim:consoleserverport_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1316,13 +1401,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class PowerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class PowerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_powerport'
|
permission_required = 'dcim.add_powerport'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
form = forms.PowerPortCreateForm
|
form = forms.PowerPortCreateForm
|
||||||
model_form = forms.PowerPortForm
|
model_form = forms.PowerPortForm
|
||||||
@ -1347,11 +1430,18 @@ class PowerPortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
default_return_url = 'dcim:powerport_list'
|
default_return_url = 'dcim:powerport_list'
|
||||||
|
|
||||||
|
|
||||||
|
class PowerPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
|
permission_required = 'dcim.change_powerport'
|
||||||
|
queryset = PowerPort.objects.all()
|
||||||
|
table = tables.PowerPortTable
|
||||||
|
form = forms.PowerPortBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_powerport'
|
permission_required = 'dcim.delete_powerport'
|
||||||
queryset = PowerPort.objects.all()
|
queryset = PowerPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.PowerPortTable
|
table = tables.PowerPortTable
|
||||||
|
default_return_url = 'dcim:powerport_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1364,13 +1454,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class PowerOutletCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_poweroutlet'
|
permission_required = 'dcim.add_poweroutlet'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
form = forms.PowerOutletCreateForm
|
form = forms.PowerOutletCreateForm
|
||||||
model_form = forms.PowerOutletForm
|
model_form = forms.PowerOutletForm
|
||||||
@ -1398,7 +1486,6 @@ class PowerOutletBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
class PowerOutletBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class PowerOutletBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
permission_required = 'dcim.change_poweroutlet'
|
permission_required = 'dcim.change_poweroutlet'
|
||||||
queryset = PowerOutlet.objects.all()
|
queryset = PowerOutlet.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.PowerOutletTable
|
table = tables.PowerOutletTable
|
||||||
form = forms.PowerOutletBulkEditForm
|
form = forms.PowerOutletBulkEditForm
|
||||||
|
|
||||||
@ -1418,8 +1505,8 @@ class PowerOutletBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView)
|
|||||||
class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_poweroutlet'
|
permission_required = 'dcim.delete_poweroutlet'
|
||||||
queryset = PowerOutlet.objects.all()
|
queryset = PowerOutlet.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.PowerOutletTable
|
table = tables.PowerOutletTable
|
||||||
|
default_return_url = 'dcim:poweroutlet_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1432,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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class InterfaceView(PermissionRequiredMixin, View):
|
class InterfaceView(PermissionRequiredMixin, View):
|
||||||
@ -1473,8 +1560,6 @@ class InterfaceView(PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
class InterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class InterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_interface'
|
permission_required = 'dcim.add_interface'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = Interface
|
model = Interface
|
||||||
form = forms.InterfaceCreateForm
|
form = forms.InterfaceCreateForm
|
||||||
model_form = forms.InterfaceForm
|
model_form = forms.InterfaceForm
|
||||||
@ -1503,7 +1588,6 @@ class InterfaceBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
permission_required = 'dcim.change_interface'
|
permission_required = 'dcim.change_interface'
|
||||||
queryset = Interface.objects.all()
|
queryset = Interface.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.InterfaceTable
|
table = tables.InterfaceTable
|
||||||
form = forms.InterfaceBulkEditForm
|
form = forms.InterfaceBulkEditForm
|
||||||
|
|
||||||
@ -1523,8 +1607,8 @@ class InterfaceBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
|
|||||||
class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_interface'
|
permission_required = 'dcim.delete_interface'
|
||||||
queryset = Interface.objects.all()
|
queryset = Interface.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.InterfaceTable
|
table = tables.InterfaceTable
|
||||||
|
default_return_url = 'dcim:interface_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1537,13 +1621,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class FrontPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class FrontPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_frontport'
|
permission_required = 'dcim.add_frontport'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
form = forms.FrontPortCreateForm
|
form = forms.FrontPortCreateForm
|
||||||
model_form = forms.FrontPortForm
|
model_form = forms.FrontPortForm
|
||||||
@ -1571,7 +1653,6 @@ class FrontPortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
class FrontPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class FrontPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
permission_required = 'dcim.change_frontport'
|
permission_required = 'dcim.change_frontport'
|
||||||
queryset = FrontPort.objects.all()
|
queryset = FrontPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.FrontPortTable
|
table = tables.FrontPortTable
|
||||||
form = forms.FrontPortBulkEditForm
|
form = forms.FrontPortBulkEditForm
|
||||||
|
|
||||||
@ -1591,8 +1672,8 @@ class FrontPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
|
|||||||
class FrontPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class FrontPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_frontport'
|
permission_required = 'dcim.delete_frontport'
|
||||||
queryset = FrontPort.objects.all()
|
queryset = FrontPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.FrontPortTable
|
table = tables.FrontPortTable
|
||||||
|
default_return_url = 'dcim:frontport_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1605,13 +1686,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class RearPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class RearPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_rearport'
|
permission_required = 'dcim.add_rearport'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = RearPort
|
model = RearPort
|
||||||
form = forms.RearPortCreateForm
|
form = forms.RearPortCreateForm
|
||||||
model_form = forms.RearPortForm
|
model_form = forms.RearPortForm
|
||||||
@ -1639,7 +1718,6 @@ class RearPortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
class RearPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
class RearPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
permission_required = 'dcim.change_rearport'
|
permission_required = 'dcim.change_rearport'
|
||||||
queryset = RearPort.objects.all()
|
queryset = RearPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.RearPortTable
|
table = tables.RearPortTable
|
||||||
form = forms.RearPortBulkEditForm
|
form = forms.RearPortBulkEditForm
|
||||||
|
|
||||||
@ -1659,8 +1737,8 @@ class RearPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
|
|||||||
class RearPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class RearPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_rearport'
|
permission_required = 'dcim.delete_rearport'
|
||||||
queryset = RearPort.objects.all()
|
queryset = RearPort.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.RearPortTable
|
table = tables.RearPortTable
|
||||||
|
default_return_url = 'dcim:rearport_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1675,13 +1753,11 @@ 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/device_component_list.html'
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class DeviceBayCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
permission_required = 'dcim.add_devicebay'
|
permission_required = 'dcim.add_devicebay'
|
||||||
parent_model = Device
|
|
||||||
parent_field = 'device'
|
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
form = forms.DeviceBayCreateForm
|
form = forms.DeviceBayCreateForm
|
||||||
model_form = forms.DeviceBayForm
|
model_form = forms.DeviceBayForm
|
||||||
@ -1784,8 +1860,8 @@ class DeviceBayBulkRenameView(PermissionRequiredMixin, BulkRenameView):
|
|||||||
class DeviceBayBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class DeviceBayBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_devicebay'
|
permission_required = 'dcim.delete_devicebay'
|
||||||
queryset = DeviceBay.objects.all()
|
queryset = DeviceBay.objects.all()
|
||||||
parent_model = Device
|
|
||||||
table = tables.DeviceBayTable
|
table = tables.DeviceBayTable
|
||||||
|
default_return_url = 'dcim:devicebay_list'
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1876,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):
|
||||||
@ -2148,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):
|
||||||
@ -2156,13 +2232,13 @@ class InventoryItemEditView(PermissionRequiredMixin, ObjectEditView):
|
|||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
model_form = forms.InventoryItemForm
|
model_form = forms.InventoryItemForm
|
||||||
|
|
||||||
def alter_obj(self, obj, request, url_args, url_kwargs):
|
|
||||||
if 'device' in url_kwargs:
|
|
||||||
obj.device = get_object_or_404(Device, pk=url_kwargs['device'])
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_return_url(self, request, obj):
|
class InventoryItemCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
return reverse('dcim:device_inventory', kwargs={'pk': obj.device.pk})
|
permission_required = 'dcim.add_inventoryitem'
|
||||||
|
model = InventoryItem
|
||||||
|
form = forms.InventoryItemCreateForm
|
||||||
|
model_form = forms.InventoryItemForm
|
||||||
|
template_name = 'dcim/device_component_add.html'
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
class InventoryItemDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
@ -2204,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):
|
||||||
@ -2448,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):
|
||||||
@ -2517,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):
|
||||||
|
@ -15,34 +15,34 @@ router = routers.DefaultRouter()
|
|||||||
router.APIRootView = ExtrasRootView
|
router.APIRootView = ExtrasRootView
|
||||||
|
|
||||||
# Field choices
|
# Field choices
|
||||||
router.register(r'_choices', views.ExtrasFieldChoicesViewSet, basename='field-choice')
|
router.register('_choices', views.ExtrasFieldChoicesViewSet, basename='field-choice')
|
||||||
|
|
||||||
# Custom field choices
|
# Custom field choices
|
||||||
router.register(r'_custom_field_choices', views.CustomFieldChoicesViewSet, basename='custom-field-choice')
|
router.register('_custom_field_choices', views.CustomFieldChoicesViewSet, basename='custom-field-choice')
|
||||||
|
|
||||||
# Graphs
|
# Graphs
|
||||||
router.register(r'graphs', views.GraphViewSet)
|
router.register('graphs', views.GraphViewSet)
|
||||||
|
|
||||||
# Export templates
|
# Export templates
|
||||||
router.register(r'export-templates', views.ExportTemplateViewSet)
|
router.register('export-templates', views.ExportTemplateViewSet)
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
router.register(r'tags', views.TagViewSet)
|
router.register('tags', views.TagViewSet)
|
||||||
|
|
||||||
# Image attachments
|
# Image attachments
|
||||||
router.register(r'image-attachments', views.ImageAttachmentViewSet)
|
router.register('image-attachments', views.ImageAttachmentViewSet)
|
||||||
|
|
||||||
# Config contexts
|
# Config contexts
|
||||||
router.register(r'config-contexts', views.ConfigContextViewSet)
|
router.register('config-contexts', views.ConfigContextViewSet)
|
||||||
|
|
||||||
# Reports
|
# Reports
|
||||||
router.register(r'reports', views.ReportViewSet, basename='report')
|
router.register('reports', views.ReportViewSet, basename='report')
|
||||||
|
|
||||||
# Scripts
|
# Scripts
|
||||||
router.register(r'scripts', views.ScriptViewSet, basename='script')
|
router.register('scripts', views.ScriptViewSet, basename='script')
|
||||||
|
|
||||||
# Change logging
|
# Change logging
|
||||||
router.register(r'object-changes', views.ObjectChangeViewSet)
|
router.register('object-changes', views.ObjectChangeViewSet)
|
||||||
|
|
||||||
app_name = 'extras-api'
|
app_name = 'extras-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
@ -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."
|
|
||||||
)
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from mptt.forms import TreeNodeMultipleChoiceField
|
||||||
from taggit.forms import TagField
|
from taggit.forms import TagField
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
|
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
|
||||||
CommentField, ContentTypeSelect, DateTimePicker, FilterChoiceField, JSONField, SlugField, StaticSelect2,
|
CommentField, ContentTypeSelect, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField,
|
||||||
BOOLEAN_WITH_BLANK_CHOICES,
|
StaticSelect2, StaticSelect2Multiple, BOOLEAN_WITH_BLANK_CHOICES,
|
||||||
)
|
)
|
||||||
from virtualization.models import Cluster, ClusterGroup
|
from virtualization.models import Cluster, ClusterGroup
|
||||||
from .choices import *
|
from .choices import *
|
||||||
@ -133,7 +134,8 @@ class CustomFieldFilterForm(forms.Form):
|
|||||||
filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
|
filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
|
||||||
)
|
)
|
||||||
for cf in custom_fields:
|
for cf in custom_fields:
|
||||||
self.fields[cf.name] = cf.to_form_field(set_initial=True, enforce_required=False)
|
field_name = 'cf_{}'.format(cf.name)
|
||||||
|
self.fields[field_name] = cf.to_form_field(set_initial=True, enforce_required=False)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -189,7 +191,61 @@ class TagBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
||||||
tags = forms.ModelMultipleChoiceField(
|
regions = TreeNodeMultipleChoiceField(
|
||||||
|
queryset=Region.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=StaticSelect2Multiple()
|
||||||
|
)
|
||||||
|
sites = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/sites/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
roles = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=DeviceRole.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/device-roles/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
platforms = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Platform.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/dcim/platforms/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cluster_groups = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=ClusterGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/virtualization/cluster-groups/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
clusters = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Cluster.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/virtualization/clusters/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
tenant_groups = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=TenantGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/tenancy/tenant-groups/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
tenants = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelectMultiple(
|
||||||
|
api_url="/api/tenancy/tenants/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
@ -203,36 +259,10 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConfigContext
|
model = ConfigContext
|
||||||
fields = [
|
fields = (
|
||||||
'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'cluster_groups',
|
'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'cluster_groups',
|
||||||
'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
|
'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
|
||||||
]
|
)
|
||||||
widgets = {
|
|
||||||
'regions': APISelectMultiple(
|
|
||||||
api_url="/api/dcim/regions/"
|
|
||||||
),
|
|
||||||
'sites': APISelectMultiple(
|
|
||||||
api_url="/api/dcim/sites/"
|
|
||||||
),
|
|
||||||
'roles': APISelectMultiple(
|
|
||||||
api_url="/api/dcim/device-roles/"
|
|
||||||
),
|
|
||||||
'platforms': APISelectMultiple(
|
|
||||||
api_url="/api/dcim/platforms/"
|
|
||||||
),
|
|
||||||
'cluster_groups': APISelectMultiple(
|
|
||||||
api_url="/api/virtualization/cluster-groups/"
|
|
||||||
),
|
|
||||||
'clusters': APISelectMultiple(
|
|
||||||
api_url="/api/virtualization/clusters/"
|
|
||||||
),
|
|
||||||
'tenant_groups': APISelectMultiple(
|
|
||||||
api_url="/api/tenancy/tenant-groups/"
|
|
||||||
),
|
|
||||||
'tenants': APISelectMultiple(
|
|
||||||
api_url="/api/tenancy/tenants/"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||||
@ -264,72 +294,81 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
region = FilterChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/regions/",
|
api_url="/api/dcim/regions/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
role = FilterChoiceField(
|
role = DynamicModelMultipleChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/device-roles/",
|
api_url="/api/dcim/device-roles/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
platform = FilterChoiceField(
|
platform = DynamicModelMultipleChoiceField(
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/platforms/",
|
api_url="/api/dcim/platforms/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
cluster_group = FilterChoiceField(
|
cluster_group = DynamicModelMultipleChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/virtualization/cluster-groups/",
|
api_url="/api/virtualization/cluster-groups/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
cluster_id = FilterChoiceField(
|
cluster_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
|
required=False,
|
||||||
label='Cluster',
|
label='Cluster',
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/virtualization/clusters/",
|
api_url="/api/virtualization/clusters/",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tenant_group = FilterChoiceField(
|
tenant_group = DynamicModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/tenancy/tenant-groups/",
|
api_url="/api/tenancy/tenant-groups/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tenant = FilterChoiceField(
|
tenant = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/tenancy/tenants/",
|
api_url="/api/tenancy/tenants/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tag = FilterChoiceField(
|
tag = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/extras/tags/",
|
api_url="/api/extras/tags/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
@ -386,11 +425,14 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
|
|||||||
)
|
)
|
||||||
action = forms.ChoiceField(
|
action = forms.ChoiceField(
|
||||||
choices=add_blank_choice(ObjectChangeActionChoices),
|
choices=add_blank_choice(ObjectChangeActionChoices),
|
||||||
required=False
|
required=False,
|
||||||
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
|
# TODO: Convert to DynamicModelMultipleChoiceField once we have an API endpoint for users
|
||||||
user = forms.ModelChoiceField(
|
user = forms.ModelChoiceField(
|
||||||
queryset=User.objects.order_by('username'),
|
queryset=User.objects.order_by('username'),
|
||||||
required=False
|
required=False,
|
||||||
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
changed_object_type = forms.ModelChoiceField(
|
changed_object_type = forms.ModelChoiceField(
|
||||||
queryset=ContentType.objects.order_by('model'),
|
queryset=ContentType.objects.order_by('model'),
|
||||||
|
111
netbox/extras/management/commands/renaturalize.py
Normal file
111
netbox/extras/management/commands/renaturalize.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
from django.apps import apps
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
|
from utilities.fields import NaturalOrderingField
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Recalculate natural ordering values for the specified models"
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'args', metavar='app_label.ModelName', nargs='*',
|
||||||
|
help='One or more specific models (each prefixed with its app_label) to renaturalize',
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_models(self, names):
|
||||||
|
"""
|
||||||
|
Compile a list of models to be renaturalized. If no names are specified, all models which have one or more
|
||||||
|
NaturalOrderingFields will be included.
|
||||||
|
"""
|
||||||
|
models = []
|
||||||
|
|
||||||
|
if names:
|
||||||
|
# Collect all NaturalOrderingFields present on the specified models
|
||||||
|
for name in names:
|
||||||
|
try:
|
||||||
|
app_label, model_name = name.split('.')
|
||||||
|
except ValueError:
|
||||||
|
raise CommandError(
|
||||||
|
"Invalid format: {}. Models must be specified in the form app_label.ModelName.".format(name)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
app_config = apps.get_app_config(app_label)
|
||||||
|
except LookupError as e:
|
||||||
|
raise CommandError(str(e))
|
||||||
|
try:
|
||||||
|
model = app_config.get_model(model_name)
|
||||||
|
except LookupError:
|
||||||
|
raise CommandError("Unknown model: {}.{}".format(app_label, model_name))
|
||||||
|
fields = [
|
||||||
|
field for field in model._meta.concrete_fields if type(field) is NaturalOrderingField
|
||||||
|
]
|
||||||
|
if not fields:
|
||||||
|
raise CommandError(
|
||||||
|
"Invalid model: {}.{} does not employ natural ordering".format(app_label, model_name)
|
||||||
|
)
|
||||||
|
models.append(
|
||||||
|
(model, fields)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Find *all* models with NaturalOrderingFields
|
||||||
|
for app_config in apps.get_app_configs():
|
||||||
|
for model in app_config.models.values():
|
||||||
|
fields = [
|
||||||
|
field for field in model._meta.concrete_fields if type(field) is NaturalOrderingField
|
||||||
|
]
|
||||||
|
if fields:
|
||||||
|
models.append(
|
||||||
|
(model, fields)
|
||||||
|
)
|
||||||
|
|
||||||
|
return models
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
|
||||||
|
models = self._get_models(args)
|
||||||
|
|
||||||
|
if options['verbosity']:
|
||||||
|
self.stdout.write("Renaturalizing {} models.".format(len(models)))
|
||||||
|
|
||||||
|
for model, fields in models:
|
||||||
|
for field in fields:
|
||||||
|
target_field = field.target_field
|
||||||
|
naturalize = field.naturalize_function
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
# Print the model and field name
|
||||||
|
if options['verbosity']:
|
||||||
|
self.stdout.write(
|
||||||
|
"{}.{} ({})... ".format(model._meta.label, field.target_field, field.name),
|
||||||
|
ending='\n' if options['verbosity'] >= 2 else ''
|
||||||
|
)
|
||||||
|
self.stdout.flush()
|
||||||
|
|
||||||
|
# Find all unique values for the field
|
||||||
|
queryset = model.objects.values_list(target_field, flat=True).order_by(target_field).distinct()
|
||||||
|
for value in queryset:
|
||||||
|
naturalized_value = naturalize(value, max_length=field.max_length)
|
||||||
|
|
||||||
|
if options['verbosity'] >= 2:
|
||||||
|
self.stdout.write(" {} -> {}".format(value, naturalized_value), ending='')
|
||||||
|
self.stdout.flush()
|
||||||
|
|
||||||
|
# Update each unique field value in bulk
|
||||||
|
changed = model.objects.filter(name=value).update(**{field.name: naturalized_value})
|
||||||
|
|
||||||
|
if options['verbosity'] >= 2:
|
||||||
|
self.stdout.write(" ({})".format(changed))
|
||||||
|
count += changed
|
||||||
|
|
||||||
|
# Print the total count of alterations for the field
|
||||||
|
if options['verbosity'] >= 2:
|
||||||
|
self.stdout.write(self.style.SUCCESS("{} {} updated ({} unique values)".format(
|
||||||
|
count, model._meta.verbose_name_plural, queryset.count()
|
||||||
|
)))
|
||||||
|
elif options['verbosity']:
|
||||||
|
self.stdout.write(self.style.SUCCESS(str(count)))
|
||||||
|
|
||||||
|
if options['verbosity']:
|
||||||
|
self.stdout.write(self.style.SUCCESS("Done."))
|
@ -48,7 +48,7 @@ class ScriptVariable:
|
|||||||
"""
|
"""
|
||||||
form_field = forms.CharField
|
form_field = forms.CharField
|
||||||
|
|
||||||
def __init__(self, label='', description='', default=None, required=True):
|
def __init__(self, label='', description='', default=None, required=True, widget=None):
|
||||||
|
|
||||||
# Initialize field attributes
|
# Initialize field attributes
|
||||||
if not hasattr(self, 'field_attrs'):
|
if not hasattr(self, 'field_attrs'):
|
||||||
@ -59,6 +59,8 @@ class ScriptVariable:
|
|||||||
self.field_attrs['help_text'] = description
|
self.field_attrs['help_text'] = description
|
||||||
if default:
|
if default:
|
||||||
self.field_attrs['initial'] = default
|
self.field_attrs['initial'] = default
|
||||||
|
if widget:
|
||||||
|
self.field_attrs['widget'] = widget
|
||||||
self.field_attrs['required'] = required
|
self.field_attrs['required'] = required
|
||||||
|
|
||||||
# Initialize the list of optional validators if none have already been defined
|
# Initialize the list of optional validators if none have already been defined
|
||||||
@ -71,7 +73,10 @@ class ScriptVariable:
|
|||||||
"""
|
"""
|
||||||
form_field = self.form_field(**self.field_attrs)
|
form_field = self.form_field(**self.field_attrs)
|
||||||
if not isinstance(form_field.widget, forms.CheckboxInput):
|
if not isinstance(form_field.widget, forms.CheckboxInput):
|
||||||
form_field.widget.attrs['class'] = 'form-control'
|
if form_field.widget.attrs and 'class' in form_field.widget.attrs.keys():
|
||||||
|
form_field.widget.attrs['class'] += ' form-control'
|
||||||
|
else:
|
||||||
|
form_field.widget.attrs['class'] = 'form-control'
|
||||||
|
|
||||||
return form_field
|
return form_field
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ from django.urls import reverse
|
|||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from extras.choices import ObjectChangeActionChoices
|
from extras.choices import ObjectChangeActionChoices
|
||||||
from extras.models import ConfigContext, ObjectChange, Tag
|
from extras.models import ConfigContext, ObjectChange, Tag
|
||||||
from utilities.testing import StandardTestCases, TestCase
|
from utilities.testing import ViewTestCases, TestCase
|
||||||
|
|
||||||
|
|
||||||
class TagTestCase(StandardTestCases.Views):
|
class TagTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Tag
|
model = Tag
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
@ -38,7 +38,7 @@ class TagTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextTestCase(StandardTestCases.Views):
|
class ConfigContextTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = ConfigContext
|
model = ConfigContext
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
|
@ -8,38 +8,38 @@ app_name = 'extras'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
path(r'tags/', views.TagListView.as_view(), name='tag_list'),
|
path('tags/', views.TagListView.as_view(), name='tag_list'),
|
||||||
path(r'tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'),
|
path('tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'),
|
||||||
path(r'tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
|
path('tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
|
||||||
path(r'tags/<str:slug>/', views.TagView.as_view(), name='tag'),
|
path('tags/<str:slug>/', views.TagView.as_view(), name='tag'),
|
||||||
path(r'tags/<str:slug>/edit/', views.TagEditView.as_view(), name='tag_edit'),
|
path('tags/<str:slug>/edit/', views.TagEditView.as_view(), name='tag_edit'),
|
||||||
path(r'tags/<str:slug>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
|
path('tags/<str:slug>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
|
||||||
path(r'tags/<str:slug>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),
|
path('tags/<str:slug>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),
|
||||||
|
|
||||||
# Config contexts
|
# Config contexts
|
||||||
path(r'config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'),
|
path('config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'),
|
||||||
path(r'config-contexts/add/', views.ConfigContextCreateView.as_view(), name='configcontext_add'),
|
path('config-contexts/add/', views.ConfigContextCreateView.as_view(), name='configcontext_add'),
|
||||||
path(r'config-contexts/edit/', views.ConfigContextBulkEditView.as_view(), name='configcontext_bulk_edit'),
|
path('config-contexts/edit/', views.ConfigContextBulkEditView.as_view(), name='configcontext_bulk_edit'),
|
||||||
path(r'config-contexts/delete/', views.ConfigContextBulkDeleteView.as_view(), name='configcontext_bulk_delete'),
|
path('config-contexts/delete/', views.ConfigContextBulkDeleteView.as_view(), name='configcontext_bulk_delete'),
|
||||||
path(r'config-contexts/<int:pk>/', views.ConfigContextView.as_view(), name='configcontext'),
|
path('config-contexts/<int:pk>/', views.ConfigContextView.as_view(), name='configcontext'),
|
||||||
path(r'config-contexts/<int:pk>/edit/', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
|
path('config-contexts/<int:pk>/edit/', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
|
||||||
path(r'config-contexts/<int:pk>/delete/', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
|
path('config-contexts/<int:pk>/delete/', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
|
||||||
|
|
||||||
# Image attachments
|
# Image attachments
|
||||||
path(r'image-attachments/<int:pk>/edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
|
path('image-attachments/<int:pk>/edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
|
||||||
path(r'image-attachments/<int:pk>/delete/', views.ImageAttachmentDeleteView.as_view(), name='imageattachment_delete'),
|
path('image-attachments/<int:pk>/delete/', views.ImageAttachmentDeleteView.as_view(), name='imageattachment_delete'),
|
||||||
|
|
||||||
# Change logging
|
# Change logging
|
||||||
path(r'changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
|
||||||
path(r'changelog/<int:pk>/', views.ObjectChangeView.as_view(), name='objectchange'),
|
path('changelog/<int:pk>/', views.ObjectChangeView.as_view(), name='objectchange'),
|
||||||
|
|
||||||
# Reports
|
# Reports
|
||||||
path(r'reports/', views.ReportListView.as_view(), name='report_list'),
|
path('reports/', views.ReportListView.as_view(), name='report_list'),
|
||||||
path(r'reports/<str:name>/', views.ReportView.as_view(), name='report'),
|
path('reports/<str:name>/', views.ReportView.as_view(), name='report'),
|
||||||
path(r'reports/<str:name>/run/', views.ReportRunView.as_view(), name='report_run'),
|
path('reports/<str:name>/run/', views.ReportRunView.as_view(), name='report_run'),
|
||||||
|
|
||||||
# Scripts
|
# Scripts
|
||||||
path(r'scripts/', views.ScriptListView.as_view(), name='script_list'),
|
path('scripts/', views.ScriptListView.as_view(), name='script_list'),
|
||||||
path(r'scripts/<str:module>/<str:name>/', views.ScriptView.as_view(), name='script'),
|
path('scripts/<str:module>/<str:name>/', views.ScriptView.as_view(), name='script'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -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):
|
||||||
|
@ -202,7 +202,7 @@ class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
|||||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
status = ChoiceField(choices=IPAddressStatusChoices, required=False)
|
status = ChoiceField(choices=IPAddressStatusChoices, required=False)
|
||||||
role = ChoiceField(choices=IPAddressRoleChoices, required=False, allow_null=True)
|
role = ChoiceField(choices=IPAddressRoleChoices, allow_blank=True, required=False)
|
||||||
interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
|
interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
|
||||||
nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
|
nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||||
nat_outside = NestedIPAddressSerializer(read_only=True)
|
nat_outside = NestedIPAddressSerializer(read_only=True)
|
||||||
@ -240,7 +240,7 @@ class AvailableIPSerializer(serializers.Serializer):
|
|||||||
class ServiceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
class ServiceSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||||
device = NestedDeviceSerializer(required=False, allow_null=True)
|
device = NestedDeviceSerializer(required=False, allow_null=True)
|
||||||
virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)
|
virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)
|
||||||
protocol = ChoiceField(choices=ServiceProtocolChoices)
|
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
|
||||||
ipaddresses = SerializedPKRelatedField(
|
ipaddresses = SerializedPKRelatedField(
|
||||||
queryset=IPAddress.objects.all(),
|
queryset=IPAddress.objects.all(),
|
||||||
serializer=NestedIPAddressSerializer,
|
serializer=NestedIPAddressSerializer,
|
||||||
|
@ -15,30 +15,30 @@ router = routers.DefaultRouter()
|
|||||||
router.APIRootView = IPAMRootView
|
router.APIRootView = IPAMRootView
|
||||||
|
|
||||||
# Field choices
|
# Field choices
|
||||||
router.register(r'_choices', views.IPAMFieldChoicesViewSet, basename='field-choice')
|
router.register('_choices', views.IPAMFieldChoicesViewSet, basename='field-choice')
|
||||||
|
|
||||||
# VRFs
|
# VRFs
|
||||||
router.register(r'vrfs', views.VRFViewSet)
|
router.register('vrfs', views.VRFViewSet)
|
||||||
|
|
||||||
# RIRs
|
# RIRs
|
||||||
router.register(r'rirs', views.RIRViewSet)
|
router.register('rirs', views.RIRViewSet)
|
||||||
|
|
||||||
# Aggregates
|
# Aggregates
|
||||||
router.register(r'aggregates', views.AggregateViewSet)
|
router.register('aggregates', views.AggregateViewSet)
|
||||||
|
|
||||||
# Prefixes
|
# Prefixes
|
||||||
router.register(r'roles', views.RoleViewSet)
|
router.register('roles', views.RoleViewSet)
|
||||||
router.register(r'prefixes', views.PrefixViewSet)
|
router.register('prefixes', views.PrefixViewSet)
|
||||||
|
|
||||||
# IP addresses
|
# IP addresses
|
||||||
router.register(r'ip-addresses', views.IPAddressViewSet)
|
router.register('ip-addresses', views.IPAddressViewSet)
|
||||||
|
|
||||||
# VLANs
|
# VLANs
|
||||||
router.register(r'vlan-groups', views.VLANGroupViewSet)
|
router.register('vlan-groups', views.VLANGroupViewSet)
|
||||||
router.register(r'vlans', views.VLANViewSet)
|
router.register('vlans', views.VLANViewSet)
|
||||||
|
|
||||||
# Services
|
# Services
|
||||||
router.register(r'services', views.ServiceViewSet)
|
router.register('services', views.ServiceViewSet)
|
||||||
|
|
||||||
app_name = 'ipam-api'
|
app_name = 'ipam-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from dcim.models import Device, Interface, Region, Site
|
|||||||
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||||
from tenancy.filters import TenancyFilterSet
|
from tenancy.filters import TenancyFilterSet
|
||||||
from utilities.filters import (
|
from utilities.filters import (
|
||||||
MultiValueCharFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter,
|
MultiValueCharFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter,
|
||||||
)
|
)
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
from .choices import *
|
from .choices import *
|
||||||
@ -304,12 +304,12 @@ class IPAddressFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedF
|
|||||||
to_field_name='rd',
|
to_field_name='rd',
|
||||||
label='VRF (RD)',
|
label='VRF (RD)',
|
||||||
)
|
)
|
||||||
device = django_filters.CharFilter(
|
device = MultiValueCharFilter(
|
||||||
method='filter_device',
|
method='filter_device',
|
||||||
field_name='name',
|
field_name='name',
|
||||||
label='Device',
|
label='Device (name)',
|
||||||
)
|
)
|
||||||
device_id = django_filters.NumberFilter(
|
device_id = MultiValueNumberFilter(
|
||||||
method='filter_device',
|
method='filter_device',
|
||||||
field_name='pk',
|
field_name='pk',
|
||||||
label='Device (ID)',
|
label='Device (ID)',
|
||||||
@ -385,8 +385,10 @@ class IPAddressFilterSet(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedF
|
|||||||
|
|
||||||
def filter_device(self, queryset, name, value):
|
def filter_device(self, queryset, name, value):
|
||||||
try:
|
try:
|
||||||
device = Device.objects.prefetch_related('device_type').get(**{name: value})
|
devices = Device.objects.prefetch_related('device_type').filter(**{'{}__in'.format(name): value})
|
||||||
vc_interface_ids = [i['id'] for i in device.vc_interfaces.values('id')]
|
vc_interface_ids = []
|
||||||
|
for device in devices:
|
||||||
|
vc_interface_ids.extend([i['id'] for i in device.vc_interfaces.values('id')])
|
||||||
return queryset.filter(interface_id__in=vc_interface_ids)
|
return queryset.filter(interface_id__in=vc_interface_ids)
|
||||||
except Device.DoesNotExist:
|
except Device.DoesNotExist:
|
||||||
return queryset.none()
|
return queryset.none()
|
||||||
|
@ -10,9 +10,10 @@ from extras.forms import (
|
|||||||
from tenancy.forms import TenancyFilterForm, TenancyForm
|
from tenancy.forms import TenancyFilterForm, TenancyForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditNullBooleanSelect, ChainedModelChoiceField,
|
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditNullBooleanSelect, CSVChoiceField,
|
||||||
CSVChoiceField, DatePicker, ExpandableIPAddressField, FilterChoiceField, FlexibleModelChoiceField, ReturnURLForm,
|
DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableIPAddressField,
|
||||||
SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES
|
FlexibleModelChoiceField, ReturnURLForm, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField,
|
||||||
|
BOOLEAN_WITH_BLANK_CHOICES,
|
||||||
)
|
)
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
from .constants import *
|
from .constants import *
|
||||||
@ -75,7 +76,7 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm
|
|||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
tenant = forms.ModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -148,6 +149,12 @@ class RIRFilterForm(BootstrapMixin, forms.Form):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class AggregateForm(BootstrapMixin, CustomFieldModelForm):
|
class AggregateForm(BootstrapMixin, CustomFieldModelForm):
|
||||||
|
rir = DynamicModelChoiceField(
|
||||||
|
queryset=RIR.objects.all(),
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/ipam/rirs/"
|
||||||
|
)
|
||||||
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
@ -162,9 +169,6 @@ class AggregateForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
'rir': "Regional Internet Registry responsible for this prefix",
|
'rir': "Regional Internet Registry responsible for this prefix",
|
||||||
}
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
'rir': APISelect(
|
|
||||||
api_url="/api/ipam/rirs/"
|
|
||||||
),
|
|
||||||
'date_added': DatePicker(),
|
'date_added': DatePicker(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +193,7 @@ class AggregateBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
|||||||
queryset=Aggregate.objects.all(),
|
queryset=Aggregate.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
rir = forms.ModelChoiceField(
|
rir = DynamicModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='RIR',
|
label='RIR',
|
||||||
@ -226,9 +230,10 @@ class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
label='Address family',
|
label='Address family',
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
rir = FilterChoiceField(
|
rir = DynamicModelMultipleChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=False,
|
||||||
label='RIR',
|
label='RIR',
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/rirs/",
|
api_url="/api/ipam/rirs/",
|
||||||
@ -268,10 +273,16 @@ class RoleCSVForm(forms.ModelForm):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
site = forms.ModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
|
queryset=VRF.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/ipam/vrfs/",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Site',
|
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
filter_for={
|
filter_for={
|
||||||
@ -283,11 +294,8 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
vlan_group = ChainedModelChoiceField(
|
vlan_group = DynamicModelChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
chains=(
|
|
||||||
('site', 'site'),
|
|
||||||
),
|
|
||||||
required=False,
|
required=False,
|
||||||
label='VLAN group',
|
label='VLAN group',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -300,12 +308,8 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
vlan = ChainedModelChoiceField(
|
vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
chains=(
|
|
||||||
('site', 'site'),
|
|
||||||
('group', 'vlan_group'),
|
|
||||||
),
|
|
||||||
required=False,
|
required=False,
|
||||||
label='VLAN',
|
label='VLAN',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -313,6 +317,13 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
display_field='display_name'
|
display_field='display_name'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
role = DynamicModelChoiceField(
|
||||||
|
queryset=Role.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/ipam/roles/"
|
||||||
|
)
|
||||||
|
)
|
||||||
tags = TagField(required=False)
|
tags = TagField(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -322,13 +333,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
'tags',
|
'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'vrf': APISelect(
|
|
||||||
api_url="/api/ipam/vrfs/"
|
|
||||||
),
|
|
||||||
'status': StaticSelect2(),
|
'status': StaticSelect2(),
|
||||||
'role': APISelect(
|
|
||||||
api_url="/api/ipam/roles/"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -439,14 +444,14 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
queryset=Prefix.objects.all(),
|
queryset=Prefix.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
site = forms.ModelChoiceField(
|
site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/dcim/sites/"
|
api_url="/api/dcim/sites/"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
vrf = forms.ModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label='VRF',
|
||||||
@ -459,7 +464,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
max_value=PREFIX_LENGTH_MAX,
|
max_value=PREFIX_LENGTH_MAX,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
tenant = forms.ModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -471,7 +476,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
role = forms.ModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -525,10 +530,10 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
label='Mask length',
|
label='Mask length',
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
vrf_id = FilterChoiceField(
|
vrf_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
|
required=False,
|
||||||
label='VRF',
|
label='VRF',
|
||||||
null_label='-- Global --',
|
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vrfs/",
|
api_url="/api/ipam/vrfs/",
|
||||||
null_option=True,
|
null_option=True,
|
||||||
@ -539,7 +544,7 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2Multiple()
|
widget=StaticSelect2Multiple()
|
||||||
)
|
)
|
||||||
region = FilterChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
@ -551,20 +556,20 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
null_label='-- None --',
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
null_option=True,
|
null_option=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
role = FilterChoiceField(
|
role = DynamicModelMultipleChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
null_label='-- None --',
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/roles/",
|
api_url="/api/ipam/roles/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
@ -594,7 +599,15 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
nat_site = forms.ModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
|
queryset=VRF.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label='VRF',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/ipam/vrfs/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
nat_site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Site',
|
label='Site',
|
||||||
@ -606,11 +619,8 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
nat_rack = ChainedModelChoiceField(
|
nat_rack = DynamicModelChoiceField(
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
chains=(
|
|
||||||
('site', 'nat_site'),
|
|
||||||
),
|
|
||||||
required=False,
|
required=False,
|
||||||
label='Rack',
|
label='Rack',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -624,12 +634,8 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
nat_device = ChainedModelChoiceField(
|
nat_device = DynamicModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
chains=(
|
|
||||||
('site', 'nat_site'),
|
|
||||||
('rack', 'nat_rack'),
|
|
||||||
),
|
|
||||||
required=False,
|
required=False,
|
||||||
label='Device',
|
label='Device',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -651,11 +657,8 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
nat_inside = ChainedModelChoiceField(
|
nat_inside = DynamicModelChoiceField(
|
||||||
queryset=IPAddress.objects.all(),
|
queryset=IPAddress.objects.all(),
|
||||||
chains=(
|
|
||||||
('interface__device', 'nat_device'),
|
|
||||||
),
|
|
||||||
required=False,
|
required=False,
|
||||||
label='IP Address',
|
label='IP Address',
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -680,9 +683,6 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect2(),
|
'status': StaticSelect2(),
|
||||||
'role': StaticSelect2(),
|
'role': StaticSelect2(),
|
||||||
'vrf': APISelect(
|
|
||||||
api_url="/api/ipam/vrfs/"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -757,6 +757,14 @@ class IPAddressBulkCreateForm(BootstrapMixin, forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
|
vrf = DynamicModelChoiceField(
|
||||||
|
queryset=VRF.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label='VRF',
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/ipam/vrfs/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
@ -766,9 +774,6 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect2(),
|
'status': StaticSelect2(),
|
||||||
'role': StaticSelect2(),
|
'role': StaticSelect2(),
|
||||||
'vrf': APISelect(
|
|
||||||
api_url="/api/ipam/vrfs/"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -904,7 +909,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
|||||||
queryset=IPAddress.objects.all(),
|
queryset=IPAddress.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
vrf = forms.ModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label='VRF',
|
||||||
@ -917,7 +922,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
|||||||
max_value=IPADDRESS_MASK_LENGTH_MAX,
|
max_value=IPADDRESS_MASK_LENGTH_MAX,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
tenant = forms.ModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -950,7 +955,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
|||||||
|
|
||||||
|
|
||||||
class IPAddressAssignForm(BootstrapMixin, forms.Form):
|
class IPAddressAssignForm(BootstrapMixin, forms.Form):
|
||||||
vrf_id = forms.ModelChoiceField(
|
vrf_id = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label='VRF',
|
||||||
@ -996,10 +1001,10 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
|
|||||||
label='Mask length',
|
label='Mask length',
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
vrf_id = FilterChoiceField(
|
vrf_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
|
required=False,
|
||||||
label='VRF',
|
label='VRF',
|
||||||
null_label='-- Global --',
|
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vrfs/",
|
api_url="/api/ipam/vrfs/",
|
||||||
null_option=True,
|
null_option=True,
|
||||||
@ -1030,6 +1035,13 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
|
|||||||
#
|
#
|
||||||
|
|
||||||
class VLANGroupForm(BootstrapMixin, forms.ModelForm):
|
class VLANGroupForm(BootstrapMixin, forms.ModelForm):
|
||||||
|
site = DynamicModelChoiceField(
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/dcim/sites/"
|
||||||
|
)
|
||||||
|
)
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1037,11 +1049,6 @@ class VLANGroupForm(BootstrapMixin, forms.ModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'site', 'name', 'slug',
|
'site', 'name', 'slug',
|
||||||
]
|
]
|
||||||
widgets = {
|
|
||||||
'site': APISelect(
|
|
||||||
api_url="/api/dcim/sites/"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupCSVForm(forms.ModelForm):
|
class VLANGroupCSVForm(forms.ModelForm):
|
||||||
@ -1065,7 +1072,7 @@ class VLANGroupCSVForm(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
||||||
region = FilterChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
@ -1077,10 +1084,10 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
null_label='-- Global --',
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
@ -1094,7 +1101,7 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
site = forms.ModelChoiceField(
|
site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -1107,17 +1114,20 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
group = ChainedModelChoiceField(
|
group = DynamicModelChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
chains=(
|
|
||||||
('site', 'site'),
|
|
||||||
),
|
|
||||||
required=False,
|
required=False,
|
||||||
label='Group',
|
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url='/api/ipam/vlan-groups/',
|
api_url='/api/ipam/vlan-groups/',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
role = DynamicModelChoiceField(
|
||||||
|
queryset=Role.objects.all(),
|
||||||
|
required=False,
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/ipam/roles/"
|
||||||
|
)
|
||||||
|
)
|
||||||
tags = TagField(required=False)
|
tags = TagField(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1135,9 +1145,6 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect2(),
|
'status': StaticSelect2(),
|
||||||
'role': APISelect(
|
|
||||||
api_url="/api/ipam/roles/"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1212,21 +1219,21 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
|||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
site = forms.ModelChoiceField(
|
site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/dcim/sites/"
|
api_url="/api/dcim/sites/"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
group = forms.ModelChoiceField(
|
group = DynamicModelChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
api_url="/api/ipam/vlan-groups/"
|
api_url="/api/ipam/vlan-groups/"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tenant = forms.ModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -1238,7 +1245,7 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
|||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
role = forms.ModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -1263,7 +1270,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
region = FilterChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
@ -1276,20 +1283,20 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
site = FilterChoiceField(
|
site = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
null_label='-- Global --',
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/dcim/sites/",
|
api_url="/api/dcim/sites/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
null_option=True,
|
null_option=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
group_id = FilterChoiceField(
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
label='VLAN group',
|
label='VLAN group',
|
||||||
null_label='-- None --',
|
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/vlan-groups/",
|
api_url="/api/ipam/vlan-groups/",
|
||||||
null_option=True,
|
null_option=True,
|
||||||
@ -1300,10 +1307,10 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2Multiple()
|
widget=StaticSelect2Multiple()
|
||||||
)
|
)
|
||||||
role = FilterChoiceField(
|
role = DynamicModelMultipleChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
null_label='-- None --',
|
required=False,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/ipam/roles/",
|
api_url="/api/ipam/roles/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
|
@ -392,13 +392,12 @@ class IPAddressTestCase(TestCase):
|
|||||||
params = {'vrf': [vrfs[0].rd, vrfs[1].rd]}
|
params = {'vrf': [vrfs[0].rd, vrfs[1].rd]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
# TODO: Test for multiple values
|
|
||||||
def test_device(self):
|
def test_device(self):
|
||||||
device = Device.objects.first()
|
devices = Device.objects.all()[:2]
|
||||||
params = {'device_id': device.pk}
|
params = {'device_id': [devices[0].pk, devices[1].pk]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
params = {'device': device.name}
|
params = {'device': [devices[0].name, devices[1].name]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_virtual_machine(self):
|
def test_virtual_machine(self):
|
||||||
vms = VirtualMachine.objects.all()[:2]
|
vms = VirtualMachine.objects.all()[:2]
|
||||||
|
176
netbox/ipam/tests/test_ordering.py
Normal file
176
netbox/ipam/tests/test_ordering.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from ipam.choices import IPAddressStatusChoices, PrefixStatusChoices
|
||||||
|
from ipam.models import IPAddress, Prefix, VRF
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
|
||||||
|
class OrderingTestBase(TestCase):
|
||||||
|
vrfs = None
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Setup the VRFs for the class as a whole
|
||||||
|
"""
|
||||||
|
self.vrfs = (VRF(name="VRF A"), VRF(name="VRF B"), VRF(name="VRF C"))
|
||||||
|
VRF.objects.bulk_create(self.vrfs)
|
||||||
|
|
||||||
|
def _compare(self, queryset, objectset):
|
||||||
|
"""
|
||||||
|
Perform the comparison of the queryset object and the object used to instantiate the queryset.
|
||||||
|
"""
|
||||||
|
for i, obj in enumerate(queryset):
|
||||||
|
self.assertEqual(obj, objectset[i])
|
||||||
|
|
||||||
|
def _compare_ne(self, queryset, objectset):
|
||||||
|
"""
|
||||||
|
Perform the comparison of the queryset object and the object used to instantiate the queryset.
|
||||||
|
"""
|
||||||
|
for i, obj in enumerate(queryset):
|
||||||
|
self.assertNotEqual(obj, objectset[i])
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixOrderingTestCase(OrderingTestBase):
|
||||||
|
|
||||||
|
def test_prefix_vrf_ordering(self):
|
||||||
|
"""
|
||||||
|
This is a very basic test, which tests both prefixes without VRFs and prefixes with VRFs
|
||||||
|
"""
|
||||||
|
# Setup VRFs
|
||||||
|
vrfa, vrfb, vrfc = self.vrfs
|
||||||
|
|
||||||
|
# Setup Prefixes
|
||||||
|
prefixes = (
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.0.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.2.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.3.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.4.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.5.0/24')),
|
||||||
|
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/8')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.2.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.3.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.4.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.2.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.3.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.4.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.2.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.3.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.2.4.0/24')),
|
||||||
|
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.0.0/12')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.0.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.2.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.3.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.16.4.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.0.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.2.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.3.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, prefix=netaddr.IPNetwork('172.17.4.0/24')),
|
||||||
|
)
|
||||||
|
|
||||||
|
Prefix.objects.bulk_create(prefixes)
|
||||||
|
|
||||||
|
# Test
|
||||||
|
self._compare(Prefix.objects.all(), prefixes)
|
||||||
|
|
||||||
|
def test_prefix_complex_ordering(self):
|
||||||
|
"""
|
||||||
|
This function tests a complex ordering of interwoven prefixes and vrfs. This is the current expected ordering of VRFs
|
||||||
|
This includes the testing of the Container status.
|
||||||
|
|
||||||
|
The proper ordering, to get proper containerization should be:
|
||||||
|
None:10.0.0.0/8
|
||||||
|
None:10.0.0.0/16
|
||||||
|
VRF A:10.0.0.0/24
|
||||||
|
VRF A:10.0.1.0/24
|
||||||
|
VRF A:10.0.1.0/25
|
||||||
|
None:10.1.0.0/16
|
||||||
|
VRF A:10.1.0.0/24
|
||||||
|
VRF A:10.1.1.0/24
|
||||||
|
None: 192.168.0.0/16
|
||||||
|
"""
|
||||||
|
# Setup VRFs
|
||||||
|
vrfa, vrfb, vrfc = self.vrfs
|
||||||
|
|
||||||
|
# Setup Prefixes
|
||||||
|
prefixes = [
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, family=4, prefix=netaddr.IPNetwork('10.0.0.0/8')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_CONTAINER, vrf=None, family=4, prefix=netaddr.IPNetwork('10.0.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('10.1.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=None, family=4, prefix=netaddr.IPNetwork('192.168.0.0/16')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.0.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.1.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.0.1.0/25')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.0.0/24')),
|
||||||
|
Prefix(status=PrefixStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, prefix=netaddr.IPNetwork('10.1.1.0/24')),
|
||||||
|
]
|
||||||
|
Prefix.objects.bulk_create(prefixes)
|
||||||
|
|
||||||
|
# Test
|
||||||
|
self._compare(Prefix.objects.all(), prefixes)
|
||||||
|
|
||||||
|
|
||||||
|
class IPAddressOrderingTestCase(OrderingTestBase):
|
||||||
|
|
||||||
|
def test_address_vrf_ordering(self):
|
||||||
|
"""
|
||||||
|
This function tests ordering with the inclusion of vrfs
|
||||||
|
"""
|
||||||
|
# Setup VRFs
|
||||||
|
vrfa, vrfb, vrfc = self.vrfs
|
||||||
|
|
||||||
|
# Setup Addresses
|
||||||
|
addresses = (
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.0.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.1.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.2.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.3.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.0.4.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.0.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.1.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.2.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.3.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.1.4.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.0.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.1.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.2.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.3.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfa, family=4, address=netaddr.IPNetwork('10.2.4.1/24')),
|
||||||
|
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.0.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.1.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.2.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.3.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.16.4.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.0.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.1.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.2.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.3.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=vrfb, family=4, address=netaddr.IPNetwork('172.17.4.1/24')),
|
||||||
|
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.0.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.1.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.2.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.3.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.4.1/24')),
|
||||||
|
IPAddress(status=IPAddressStatusChoices.STATUS_ACTIVE, vrf=None, family=4, address=netaddr.IPNetwork('192.168.5.1/24')),
|
||||||
|
)
|
||||||
|
IPAddress.objects.bulk_create(addresses)
|
||||||
|
|
||||||
|
# Test
|
||||||
|
self._compare(IPAddress.objects.all(), addresses)
|
@ -5,10 +5,10 @@ from netaddr import IPNetwork
|
|||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
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.testing import StandardTestCases
|
from utilities.testing import ViewTestCases
|
||||||
|
|
||||||
|
|
||||||
class VRFTestCase(StandardTestCases.Views):
|
class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = VRF
|
model = VRF
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -43,14 +43,9 @@ class VRFTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RIRTestCase(StandardTestCases.Views):
|
class RIRTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = RIR
|
model = RIR
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -74,7 +69,7 @@ class RIRTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AggregateTestCase(StandardTestCases.Views):
|
class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Aggregate
|
model = Aggregate
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -115,14 +110,9 @@ class AggregateTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RoleTestCase(StandardTestCases.Views):
|
class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = Role
|
model = Role
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -147,7 +137,7 @@ class RoleTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PrefixTestCase(StandardTestCases.Views):
|
class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Prefix
|
model = Prefix
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -207,7 +197,7 @@ class PrefixTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class IPAddressTestCase(StandardTestCases.Views):
|
class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -254,14 +244,9 @@ class IPAddressTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupTestCase(StandardTestCases.Views):
|
class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = VLANGroup
|
model = VLANGroup
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -287,7 +272,7 @@ class VLANGroupTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VLANTestCase(StandardTestCases.Views):
|
class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -346,7 +331,7 @@ class VLANTestCase(StandardTestCases.Views):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ServiceTestCase(StandardTestCases.Views):
|
class ServiceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Service
|
model = Service
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
|
@ -8,97 +8,97 @@ app_name = 'ipam'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
# VRFs
|
# VRFs
|
||||||
path(r'vrfs/', views.VRFListView.as_view(), name='vrf_list'),
|
path('vrfs/', views.VRFListView.as_view(), name='vrf_list'),
|
||||||
path(r'vrfs/add/', views.VRFCreateView.as_view(), name='vrf_add'),
|
path('vrfs/add/', views.VRFCreateView.as_view(), name='vrf_add'),
|
||||||
path(r'vrfs/import/', views.VRFBulkImportView.as_view(), name='vrf_import'),
|
path('vrfs/import/', views.VRFBulkImportView.as_view(), name='vrf_import'),
|
||||||
path(r'vrfs/edit/', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'),
|
path('vrfs/edit/', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'),
|
||||||
path(r'vrfs/delete/', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'),
|
path('vrfs/delete/', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'),
|
||||||
path(r'vrfs/<int:pk>/', views.VRFView.as_view(), name='vrf'),
|
path('vrfs/<int:pk>/', views.VRFView.as_view(), name='vrf'),
|
||||||
path(r'vrfs/<int:pk>/edit/', views.VRFEditView.as_view(), name='vrf_edit'),
|
path('vrfs/<int:pk>/edit/', views.VRFEditView.as_view(), name='vrf_edit'),
|
||||||
path(r'vrfs/<int:pk>/delete/', views.VRFDeleteView.as_view(), name='vrf_delete'),
|
path('vrfs/<int:pk>/delete/', views.VRFDeleteView.as_view(), name='vrf_delete'),
|
||||||
path(r'vrfs/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vrf_changelog', kwargs={'model': VRF}),
|
path('vrfs/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vrf_changelog', kwargs={'model': VRF}),
|
||||||
|
|
||||||
# RIRs
|
# RIRs
|
||||||
path(r'rirs/', views.RIRListView.as_view(), name='rir_list'),
|
path('rirs/', views.RIRListView.as_view(), name='rir_list'),
|
||||||
path(r'rirs/add/', views.RIRCreateView.as_view(), name='rir_add'),
|
path('rirs/add/', views.RIRCreateView.as_view(), name='rir_add'),
|
||||||
path(r'rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'),
|
path('rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'),
|
||||||
path(r'rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
|
path('rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
|
||||||
path(r'rirs/<slug:slug>/edit/', views.RIREditView.as_view(), name='rir_edit'),
|
path('rirs/<slug:slug>/edit/', views.RIREditView.as_view(), name='rir_edit'),
|
||||||
path(r'vrfs/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='rir_changelog', kwargs={'model': RIR}),
|
path('vrfs/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='rir_changelog', kwargs={'model': RIR}),
|
||||||
|
|
||||||
# Aggregates
|
# Aggregates
|
||||||
path(r'aggregates/', views.AggregateListView.as_view(), name='aggregate_list'),
|
path('aggregates/', views.AggregateListView.as_view(), name='aggregate_list'),
|
||||||
path(r'aggregates/add/', views.AggregateCreateView.as_view(), name='aggregate_add'),
|
path('aggregates/add/', views.AggregateCreateView.as_view(), name='aggregate_add'),
|
||||||
path(r'aggregates/import/', views.AggregateBulkImportView.as_view(), name='aggregate_import'),
|
path('aggregates/import/', views.AggregateBulkImportView.as_view(), name='aggregate_import'),
|
||||||
path(r'aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
|
path('aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
|
||||||
path(r'aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
|
path('aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
|
||||||
path(r'aggregates/<int:pk>/', views.AggregateView.as_view(), name='aggregate'),
|
path('aggregates/<int:pk>/', views.AggregateView.as_view(), name='aggregate'),
|
||||||
path(r'aggregates/<int:pk>/edit/', views.AggregateEditView.as_view(), name='aggregate_edit'),
|
path('aggregates/<int:pk>/edit/', views.AggregateEditView.as_view(), name='aggregate_edit'),
|
||||||
path(r'aggregates/<int:pk>/delete/', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
|
path('aggregates/<int:pk>/delete/', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
|
||||||
path(r'aggregates/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='aggregate_changelog', kwargs={'model': Aggregate}),
|
path('aggregates/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='aggregate_changelog', kwargs={'model': Aggregate}),
|
||||||
|
|
||||||
# Roles
|
# Roles
|
||||||
path(r'roles/', views.RoleListView.as_view(), name='role_list'),
|
path('roles/', views.RoleListView.as_view(), name='role_list'),
|
||||||
path(r'roles/add/', views.RoleCreateView.as_view(), name='role_add'),
|
path('roles/add/', views.RoleCreateView.as_view(), name='role_add'),
|
||||||
path(r'roles/import/', views.RoleBulkImportView.as_view(), name='role_import'),
|
path('roles/import/', views.RoleBulkImportView.as_view(), name='role_import'),
|
||||||
path(r'roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
|
path('roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
|
||||||
path(r'roles/<slug:slug>/edit/', views.RoleEditView.as_view(), name='role_edit'),
|
path('roles/<slug:slug>/edit/', views.RoleEditView.as_view(), name='role_edit'),
|
||||||
path(r'roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='role_changelog', kwargs={'model': Role}),
|
path('roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='role_changelog', kwargs={'model': Role}),
|
||||||
|
|
||||||
# Prefixes
|
# Prefixes
|
||||||
path(r'prefixes/', views.PrefixListView.as_view(), name='prefix_list'),
|
path('prefixes/', views.PrefixListView.as_view(), name='prefix_list'),
|
||||||
path(r'prefixes/add/', views.PrefixCreateView.as_view(), name='prefix_add'),
|
path('prefixes/add/', views.PrefixCreateView.as_view(), name='prefix_add'),
|
||||||
path(r'prefixes/import/', views.PrefixBulkImportView.as_view(), name='prefix_import'),
|
path('prefixes/import/', views.PrefixBulkImportView.as_view(), name='prefix_import'),
|
||||||
path(r'prefixes/edit/', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'),
|
path('prefixes/edit/', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'),
|
||||||
path(r'prefixes/delete/', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'),
|
path('prefixes/delete/', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'),
|
||||||
path(r'prefixes/<int:pk>/', views.PrefixView.as_view(), name='prefix'),
|
path('prefixes/<int:pk>/', views.PrefixView.as_view(), name='prefix'),
|
||||||
path(r'prefixes/<int:pk>/edit/', views.PrefixEditView.as_view(), name='prefix_edit'),
|
path('prefixes/<int:pk>/edit/', views.PrefixEditView.as_view(), name='prefix_edit'),
|
||||||
path(r'prefixes/<int:pk>/delete/', views.PrefixDeleteView.as_view(), name='prefix_delete'),
|
path('prefixes/<int:pk>/delete/', views.PrefixDeleteView.as_view(), name='prefix_delete'),
|
||||||
path(r'prefixes/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}),
|
path('prefixes/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='prefix_changelog', kwargs={'model': Prefix}),
|
||||||
path(r'prefixes/<int:pk>/prefixes/', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'),
|
path('prefixes/<int:pk>/prefixes/', views.PrefixPrefixesView.as_view(), name='prefix_prefixes'),
|
||||||
path(r'prefixes/<int:pk>/ip-addresses/', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'),
|
path('prefixes/<int:pk>/ip-addresses/', views.PrefixIPAddressesView.as_view(), name='prefix_ipaddresses'),
|
||||||
|
|
||||||
# IP addresses
|
# IP addresses
|
||||||
path(r'ip-addresses/', views.IPAddressListView.as_view(), name='ipaddress_list'),
|
path('ip-addresses/', views.IPAddressListView.as_view(), name='ipaddress_list'),
|
||||||
path(r'ip-addresses/add/', views.IPAddressCreateView.as_view(), name='ipaddress_add'),
|
path('ip-addresses/add/', views.IPAddressCreateView.as_view(), name='ipaddress_add'),
|
||||||
path(r'ip-addresses/bulk-add/', views.IPAddressBulkCreateView.as_view(), name='ipaddress_bulk_add'),
|
path('ip-addresses/bulk-add/', views.IPAddressBulkCreateView.as_view(), name='ipaddress_bulk_add'),
|
||||||
path(r'ip-addresses/import/', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
|
path('ip-addresses/import/', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
|
||||||
path(r'ip-addresses/edit/', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
|
path('ip-addresses/edit/', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
|
||||||
path(r'ip-addresses/delete/', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
path('ip-addresses/delete/', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
||||||
path(r'ip-addresses/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='ipaddress_changelog', kwargs={'model': IPAddress}),
|
path('ip-addresses/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='ipaddress_changelog', kwargs={'model': IPAddress}),
|
||||||
path(r'ip-addresses/assign/', views.IPAddressAssignView.as_view(), name='ipaddress_assign'),
|
path('ip-addresses/assign/', views.IPAddressAssignView.as_view(), name='ipaddress_assign'),
|
||||||
path(r'ip-addresses/<int:pk>/', views.IPAddressView.as_view(), name='ipaddress'),
|
path('ip-addresses/<int:pk>/', views.IPAddressView.as_view(), name='ipaddress'),
|
||||||
path(r'ip-addresses/<int:pk>/edit/', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
|
path('ip-addresses/<int:pk>/edit/', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
|
||||||
path(r'ip-addresses/<int:pk>/delete/', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
|
path('ip-addresses/<int:pk>/delete/', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
|
||||||
|
|
||||||
# VLAN groups
|
# VLAN groups
|
||||||
path(r'vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'),
|
path('vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'),
|
||||||
path(r'vlan-groups/add/', views.VLANGroupCreateView.as_view(), name='vlangroup_add'),
|
path('vlan-groups/add/', views.VLANGroupCreateView.as_view(), name='vlangroup_add'),
|
||||||
path(r'vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
path('vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
||||||
path(r'vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
path('vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
||||||
path(r'vlan-groups/<int:pk>/edit/', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
path('vlan-groups/<int:pk>/edit/', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
||||||
path(r'vlan-groups/<int:pk>/vlans/', views.VLANGroupVLANsView.as_view(), name='vlangroup_vlans'),
|
path('vlan-groups/<int:pk>/vlans/', views.VLANGroupVLANsView.as_view(), name='vlangroup_vlans'),
|
||||||
path(r'vlan-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlangroup_changelog', kwargs={'model': VLANGroup}),
|
path('vlan-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlangroup_changelog', kwargs={'model': VLANGroup}),
|
||||||
|
|
||||||
# VLANs
|
# VLANs
|
||||||
path(r'vlans/', views.VLANListView.as_view(), name='vlan_list'),
|
path('vlans/', views.VLANListView.as_view(), name='vlan_list'),
|
||||||
path(r'vlans/add/', views.VLANCreateView.as_view(), name='vlan_add'),
|
path('vlans/add/', views.VLANCreateView.as_view(), name='vlan_add'),
|
||||||
path(r'vlans/import/', views.VLANBulkImportView.as_view(), name='vlan_import'),
|
path('vlans/import/', views.VLANBulkImportView.as_view(), name='vlan_import'),
|
||||||
path(r'vlans/edit/', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
|
path('vlans/edit/', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
|
||||||
path(r'vlans/delete/', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
|
path('vlans/delete/', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
|
||||||
path(r'vlans/<int:pk>/', views.VLANView.as_view(), name='vlan'),
|
path('vlans/<int:pk>/', views.VLANView.as_view(), name='vlan'),
|
||||||
path(r'vlans/<int:pk>/members/', views.VLANMembersView.as_view(), name='vlan_members'),
|
path('vlans/<int:pk>/members/', views.VLANMembersView.as_view(), name='vlan_members'),
|
||||||
path(r'vlans/<int:pk>/edit/', views.VLANEditView.as_view(), name='vlan_edit'),
|
path('vlans/<int:pk>/edit/', views.VLANEditView.as_view(), name='vlan_edit'),
|
||||||
path(r'vlans/<int:pk>/delete/', views.VLANDeleteView.as_view(), name='vlan_delete'),
|
path('vlans/<int:pk>/delete/', views.VLANDeleteView.as_view(), name='vlan_delete'),
|
||||||
path(r'vlans/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlan_changelog', kwargs={'model': VLAN}),
|
path('vlans/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vlan_changelog', kwargs={'model': VLAN}),
|
||||||
|
|
||||||
# Services
|
# Services
|
||||||
path(r'services/', views.ServiceListView.as_view(), name='service_list'),
|
path('services/', views.ServiceListView.as_view(), name='service_list'),
|
||||||
path(r'services/edit/', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'),
|
path('services/edit/', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'),
|
||||||
path(r'services/delete/', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'),
|
path('services/delete/', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'),
|
||||||
path(r'services/<int:pk>/', views.ServiceView.as_view(), name='service'),
|
path('services/<int:pk>/', views.ServiceView.as_view(), name='service'),
|
||||||
path(r'services/<int:pk>/edit/', views.ServiceEditView.as_view(), name='service_edit'),
|
path('services/<int:pk>/edit/', views.ServiceEditView.as_view(), name='service_edit'),
|
||||||
path(r'services/<int:pk>/delete/', views.ServiceDeleteView.as_view(), name='service_delete'),
|
path('services/<int:pk>/delete/', views.ServiceDeleteView.as_view(), name='service_delete'),
|
||||||
path(r'services/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='service_changelog', kwargs={'model': Service}),
|
path('services/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='service_changelog', kwargs={'model': Service}),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -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):
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local']
|
# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local']
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
# PostgreSQL database configuration.
|
# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters:
|
||||||
|
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
|
||||||
DATABASE = {
|
DATABASE = {
|
||||||
'NAME': 'netbox', # Database name
|
'NAME': 'netbox', # Database name
|
||||||
'USER': '', # PostgreSQL username
|
'USER': '', # PostgreSQL username
|
||||||
@ -27,6 +28,9 @@ REDIS = {
|
|||||||
'webhooks': {
|
'webhooks': {
|
||||||
'HOST': 'localhost',
|
'HOST': 'localhost',
|
||||||
'PORT': 6379,
|
'PORT': 6379,
|
||||||
|
# Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel
|
||||||
|
# 'SENTINELS': [('mysentinel.redis.example.com', 6379)],
|
||||||
|
# 'SENTINEL_SERVICE': 'netbox',
|
||||||
'PASSWORD': '',
|
'PASSWORD': '',
|
||||||
'DATABASE': 0,
|
'DATABASE': 0,
|
||||||
'DEFAULT_TIMEOUT': 300,
|
'DEFAULT_TIMEOUT': 300,
|
||||||
@ -35,6 +39,9 @@ REDIS = {
|
|||||||
'caching': {
|
'caching': {
|
||||||
'HOST': 'localhost',
|
'HOST': 'localhost',
|
||||||
'PORT': 6379,
|
'PORT': 6379,
|
||||||
|
# Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel
|
||||||
|
# 'SENTINELS': [('mysentinel.redis.example.com', 6379)],
|
||||||
|
# 'SENTINEL_SERVICE': 'netbox',
|
||||||
'PASSWORD': '',
|
'PASSWORD': '',
|
||||||
'DATABASE': 1,
|
'DATABASE': 1,
|
||||||
'DEFAULT_TIMEOUT': 300,
|
'DEFAULT_TIMEOUT': 300,
|
||||||
|
@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||||||
# Environment setup
|
# Environment setup
|
||||||
#
|
#
|
||||||
|
|
||||||
VERSION = '2.7.5-dev'
|
VERSION = '2.7.7-dev'
|
||||||
|
|
||||||
# Hostname
|
# Hostname
|
||||||
HOSTNAME = platform.node()
|
HOSTNAME = platform.node()
|
||||||
@ -170,18 +170,31 @@ if 'caching' not in REDIS:
|
|||||||
WEBHOOKS_REDIS = REDIS.get('webhooks', {})
|
WEBHOOKS_REDIS = REDIS.get('webhooks', {})
|
||||||
WEBHOOKS_REDIS_HOST = WEBHOOKS_REDIS.get('HOST', 'localhost')
|
WEBHOOKS_REDIS_HOST = WEBHOOKS_REDIS.get('HOST', 'localhost')
|
||||||
WEBHOOKS_REDIS_PORT = WEBHOOKS_REDIS.get('PORT', 6379)
|
WEBHOOKS_REDIS_PORT = WEBHOOKS_REDIS.get('PORT', 6379)
|
||||||
|
WEBHOOKS_REDIS_SENTINELS = WEBHOOKS_REDIS.get('SENTINELS', [])
|
||||||
|
WEBHOOKS_REDIS_USING_SENTINEL = all([
|
||||||
|
isinstance(WEBHOOKS_REDIS_SENTINELS, (list, tuple)),
|
||||||
|
len(WEBHOOKS_REDIS_SENTINELS) > 0
|
||||||
|
])
|
||||||
|
WEBHOOKS_REDIS_SENTINEL_SERVICE = WEBHOOKS_REDIS.get('SENTINEL_SERVICE', 'default')
|
||||||
WEBHOOKS_REDIS_PASSWORD = WEBHOOKS_REDIS.get('PASSWORD', '')
|
WEBHOOKS_REDIS_PASSWORD = WEBHOOKS_REDIS.get('PASSWORD', '')
|
||||||
WEBHOOKS_REDIS_DATABASE = WEBHOOKS_REDIS.get('DATABASE', 0)
|
WEBHOOKS_REDIS_DATABASE = WEBHOOKS_REDIS.get('DATABASE', 0)
|
||||||
WEBHOOKS_REDIS_DEFAULT_TIMEOUT = WEBHOOKS_REDIS.get('DEFAULT_TIMEOUT', 300)
|
WEBHOOKS_REDIS_DEFAULT_TIMEOUT = WEBHOOKS_REDIS.get('DEFAULT_TIMEOUT', 300)
|
||||||
WEBHOOKS_REDIS_SSL = WEBHOOKS_REDIS.get('SSL', False)
|
WEBHOOKS_REDIS_SSL = WEBHOOKS_REDIS.get('SSL', False)
|
||||||
|
|
||||||
|
|
||||||
CACHING_REDIS = REDIS.get('caching', {})
|
CACHING_REDIS = REDIS.get('caching', {})
|
||||||
CACHING_REDIS_HOST = WEBHOOKS_REDIS.get('HOST', 'localhost')
|
CACHING_REDIS_HOST = CACHING_REDIS.get('HOST', 'localhost')
|
||||||
CACHING_REDIS_PORT = WEBHOOKS_REDIS.get('PORT', 6379)
|
CACHING_REDIS_PORT = CACHING_REDIS.get('PORT', 6379)
|
||||||
CACHING_REDIS_PASSWORD = WEBHOOKS_REDIS.get('PASSWORD', '')
|
CACHING_REDIS_SENTINELS = CACHING_REDIS.get('SENTINELS', [])
|
||||||
CACHING_REDIS_DATABASE = WEBHOOKS_REDIS.get('DATABASE', 0)
|
CACHING_REDIS_USING_SENTINEL = all([
|
||||||
CACHING_REDIS_DEFAULT_TIMEOUT = WEBHOOKS_REDIS.get('DEFAULT_TIMEOUT', 300)
|
isinstance(CACHING_REDIS_SENTINELS, (list, tuple)),
|
||||||
CACHING_REDIS_SSL = WEBHOOKS_REDIS.get('SSL', False)
|
len(CACHING_REDIS_SENTINELS) > 0
|
||||||
|
])
|
||||||
|
CACHING_REDIS_SENTINEL_SERVICE = CACHING_REDIS.get('SENTINEL_SERVICE', 'default')
|
||||||
|
CACHING_REDIS_PASSWORD = CACHING_REDIS.get('PASSWORD', '')
|
||||||
|
CACHING_REDIS_DATABASE = CACHING_REDIS.get('DATABASE', 0)
|
||||||
|
CACHING_REDIS_DEFAULT_TIMEOUT = CACHING_REDIS.get('DEFAULT_TIMEOUT', 300)
|
||||||
|
CACHING_REDIS_SSL = CACHING_REDIS.get('SSL', False)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -394,28 +407,35 @@ if LDAP_CONFIG is not None:
|
|||||||
#
|
#
|
||||||
# Caching
|
# Caching
|
||||||
#
|
#
|
||||||
|
if CACHING_REDIS_USING_SENTINEL:
|
||||||
if CACHING_REDIS_SSL:
|
CACHEOPS_SENTINEL = {
|
||||||
REDIS_CACHE_CON_STRING = 'rediss://'
|
'locations': CACHING_REDIS_SENTINELS,
|
||||||
|
'service_name': CACHING_REDIS_SENTINEL_SERVICE,
|
||||||
|
'db': CACHING_REDIS_DATABASE,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
REDIS_CACHE_CON_STRING = 'redis://'
|
if CACHING_REDIS_SSL:
|
||||||
|
REDIS_CACHE_CON_STRING = 'rediss://'
|
||||||
|
else:
|
||||||
|
REDIS_CACHE_CON_STRING = 'redis://'
|
||||||
|
|
||||||
if CACHING_REDIS_PASSWORD:
|
if CACHING_REDIS_PASSWORD:
|
||||||
REDIS_CACHE_CON_STRING = '{}:{}@'.format(REDIS_CACHE_CON_STRING, CACHING_REDIS_PASSWORD)
|
REDIS_CACHE_CON_STRING = '{}:{}@'.format(REDIS_CACHE_CON_STRING, CACHING_REDIS_PASSWORD)
|
||||||
|
|
||||||
REDIS_CACHE_CON_STRING = '{}{}:{}/{}'.format(
|
REDIS_CACHE_CON_STRING = '{}{}:{}/{}'.format(
|
||||||
REDIS_CACHE_CON_STRING,
|
REDIS_CACHE_CON_STRING,
|
||||||
CACHING_REDIS_HOST,
|
CACHING_REDIS_HOST,
|
||||||
CACHING_REDIS_PORT,
|
CACHING_REDIS_PORT,
|
||||||
CACHING_REDIS_DATABASE
|
CACHING_REDIS_DATABASE
|
||||||
)
|
)
|
||||||
|
CACHEOPS_REDIS = REDIS_CACHE_CON_STRING
|
||||||
|
|
||||||
if not CACHE_TIMEOUT:
|
if not CACHE_TIMEOUT:
|
||||||
CACHEOPS_ENABLED = False
|
CACHEOPS_ENABLED = False
|
||||||
else:
|
else:
|
||||||
CACHEOPS_ENABLED = True
|
CACHEOPS_ENABLED = True
|
||||||
|
|
||||||
CACHEOPS_REDIS = REDIS_CACHE_CON_STRING
|
|
||||||
CACHEOPS_DEFAULTS = {
|
CACHEOPS_DEFAULTS = {
|
||||||
'timeout': CACHE_TIMEOUT
|
'timeout': CACHE_TIMEOUT
|
||||||
}
|
}
|
||||||
@ -534,6 +554,15 @@ RQ_QUEUES = {
|
|||||||
'PASSWORD': WEBHOOKS_REDIS_PASSWORD,
|
'PASSWORD': WEBHOOKS_REDIS_PASSWORD,
|
||||||
'DEFAULT_TIMEOUT': WEBHOOKS_REDIS_DEFAULT_TIMEOUT,
|
'DEFAULT_TIMEOUT': WEBHOOKS_REDIS_DEFAULT_TIMEOUT,
|
||||||
'SSL': WEBHOOKS_REDIS_SSL,
|
'SSL': WEBHOOKS_REDIS_SSL,
|
||||||
|
} if not WEBHOOKS_REDIS_USING_SENTINEL else {
|
||||||
|
'SENTINELS': WEBHOOKS_REDIS_SENTINELS,
|
||||||
|
'MASTER_NAME': WEBHOOKS_REDIS_SENTINEL_SERVICE,
|
||||||
|
'DB': WEBHOOKS_REDIS_DATABASE,
|
||||||
|
'PASSWORD': WEBHOOKS_REDIS_PASSWORD,
|
||||||
|
'SOCKET_TIMEOUT': None,
|
||||||
|
'CONNECTION_KWARGS': {
|
||||||
|
'socket_connect_timeout': WEBHOOKS_REDIS_DEFAULT_TIMEOUT
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,49 +26,49 @@ schema_view = get_schema_view(
|
|||||||
_patterns = [
|
_patterns = [
|
||||||
|
|
||||||
# Base views
|
# Base views
|
||||||
path(r'', HomeView.as_view(), name='home'),
|
path('', HomeView.as_view(), name='home'),
|
||||||
path(r'search/', SearchView.as_view(), name='search'),
|
path('search/', SearchView.as_view(), name='search'),
|
||||||
|
|
||||||
# Login/logout
|
# Login/logout
|
||||||
path(r'login/', LoginView.as_view(), name='login'),
|
path('login/', LoginView.as_view(), name='login'),
|
||||||
path(r'logout/', LogoutView.as_view(), name='logout'),
|
path('logout/', LogoutView.as_view(), name='logout'),
|
||||||
|
|
||||||
# Apps
|
# Apps
|
||||||
path(r'circuits/', include('circuits.urls')),
|
path('circuits/', include('circuits.urls')),
|
||||||
path(r'dcim/', include('dcim.urls')),
|
path('dcim/', include('dcim.urls')),
|
||||||
path(r'extras/', include('extras.urls')),
|
path('extras/', include('extras.urls')),
|
||||||
path(r'ipam/', include('ipam.urls')),
|
path('ipam/', include('ipam.urls')),
|
||||||
path(r'secrets/', include('secrets.urls')),
|
path('secrets/', include('secrets.urls')),
|
||||||
path(r'tenancy/', include('tenancy.urls')),
|
path('tenancy/', include('tenancy.urls')),
|
||||||
path(r'user/', include('users.urls')),
|
path('user/', include('users.urls')),
|
||||||
path(r'virtualization/', include('virtualization.urls')),
|
path('virtualization/', include('virtualization.urls')),
|
||||||
|
|
||||||
# API
|
# API
|
||||||
path(r'api/', APIRootView.as_view(), name='api-root'),
|
path('api/', APIRootView.as_view(), name='api-root'),
|
||||||
path(r'api/circuits/', include('circuits.api.urls')),
|
path('api/circuits/', include('circuits.api.urls')),
|
||||||
path(r'api/dcim/', include('dcim.api.urls')),
|
path('api/dcim/', include('dcim.api.urls')),
|
||||||
path(r'api/extras/', include('extras.api.urls')),
|
path('api/extras/', include('extras.api.urls')),
|
||||||
path(r'api/ipam/', include('ipam.api.urls')),
|
path('api/ipam/', include('ipam.api.urls')),
|
||||||
path(r'api/secrets/', include('secrets.api.urls')),
|
path('api/secrets/', include('secrets.api.urls')),
|
||||||
path(r'api/tenancy/', include('tenancy.api.urls')),
|
path('api/tenancy/', include('tenancy.api.urls')),
|
||||||
path(r'api/virtualization/', include('virtualization.api.urls')),
|
path('api/virtualization/', include('virtualization.api.urls')),
|
||||||
path(r'api/docs/', schema_view.with_ui('swagger'), name='api_docs'),
|
path('api/docs/', schema_view.with_ui('swagger'), name='api_docs'),
|
||||||
path(r'api/redoc/', schema_view.with_ui('redoc'), name='api_redocs'),
|
path('api/redoc/', schema_view.with_ui('redoc'), name='api_redocs'),
|
||||||
re_path(r'^api/swagger(?P<format>.json|.yaml)$', schema_view.without_ui(), name='schema_swagger'),
|
re_path(r'^api/swagger(?P<format>.json|.yaml)$', schema_view.without_ui(), name='schema_swagger'),
|
||||||
|
|
||||||
# Serving static media in Django to pipe it through LoginRequiredMiddleware
|
# Serving static media in Django to pipe it through LoginRequiredMiddleware
|
||||||
path(r'media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
|
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
|
||||||
|
|
||||||
# Admin
|
# Admin
|
||||||
path(r'admin/', admin_site.urls),
|
path('admin/', admin_site.urls),
|
||||||
path(r'admin/webhook-backend-status/', include('django_rq.urls')),
|
path('admin/webhook-backend-status/', include('django_rq.urls')),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
_patterns += [
|
_patterns += [
|
||||||
path(r'__debug__/', include(debug_toolbar.urls)),
|
path('__debug__/', include(debug_toolbar.urls)),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.METRICS_ENABLED:
|
if settings.METRICS_ENABLED:
|
||||||
@ -78,7 +78,7 @@ if settings.METRICS_ENABLED:
|
|||||||
|
|
||||||
# Prepend BASE_PATH
|
# Prepend BASE_PATH
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(r'{}'.format(settings.BASE_PATH), include(_patterns))
|
path('{}'.format(settings.BASE_PATH), include(_patterns))
|
||||||
]
|
]
|
||||||
|
|
||||||
handler500 = 'utilities.views.server_error'
|
handler500 = 'utilities.views.server_error'
|
||||||
|
@ -252,7 +252,7 @@ class HomeView(View):
|
|||||||
'search_form': SearchForm(),
|
'search_form': SearchForm(),
|
||||||
'stats': stats,
|
'stats': stats,
|
||||||
'report_results': ReportResult.objects.order_by('-created')[:10],
|
'report_results': ReportResult.objects.order_by('-created')[:10],
|
||||||
'changelog': ObjectChange.objects.prefetch_related('user', 'changed_object_type')[:50]
|
'changelog': ObjectChange.objects.prefetch_related('user', 'changed_object_type')[:15]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,8 +62,20 @@ footer p {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scroll the drop-down menus at or above 768px wide to match bootstrap's behavior for hiding dropdown menus */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.navbar-nav>li>ul {
|
||||||
|
max-height: calc(80vh - 50px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Collapse the nav menu on displays less than 980px wide */
|
/* Collapse the nav menu on displays less than 980px wide */
|
||||||
@media (max-width: 979px) {
|
@media (max-width: 979px) {
|
||||||
|
#navbar {
|
||||||
|
max-height: calc(80vh - 50px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.navbar-header {
|
.navbar-header {
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
@ -56,3 +56,12 @@ text {
|
|||||||
.blocked:hover+.add-device {
|
.blocked:hover+.add-device {
|
||||||
fill: none;
|
fill: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px 0px;
|
||||||
|
|
||||||
|
fill: #c0c0c0;
|
||||||
|
font-size: 10px;
|
||||||
|
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -220,19 +223,19 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( record.group !== undefined && record.group !== null && record.site !== undefined && record.site !== null ) {
|
if( record.group !== undefined && record.group !== null && record.site !== undefined && record.site !== null ) {
|
||||||
results[record.site.name + ":" + record.group.name] = results[record.site.name + ":" + record.group.name] || { text: record.site.name + " / " + record.group.name, children: [] }
|
results[record.site.name + ":" + record.group.name] = results[record.site.name + ":" + record.group.name] || { text: record.site.name + " / " + record.group.name, children: [] };
|
||||||
results[record.site.name + ":" + record.group.name].children.push(record);
|
results[record.site.name + ":" + record.group.name].children.push(record);
|
||||||
}
|
}
|
||||||
else if( record.group !== undefined && record.group !== null ) {
|
else if( record.group !== undefined && record.group !== null ) {
|
||||||
results[record.group.name] = results[record.group.name] || { text: record.group.name, children: [] }
|
results[record.group.name] = results[record.group.name] || { text: record.group.name, children: [] };
|
||||||
results[record.group.name].children.push(record);
|
results[record.group.name].children.push(record);
|
||||||
}
|
}
|
||||||
else if( record.site !== undefined && record.site !== null ) {
|
else if( record.site !== undefined && record.site !== null ) {
|
||||||
results[record.site.name] = results[record.site.name] || { text: record.site.name, children: [] }
|
results[record.site.name] = results[record.site.name] || { text: record.site.name, children: [] };
|
||||||
results[record.site.name].children.push(record);
|
results[record.site.name].children.push(record);
|
||||||
}
|
}
|
||||||
else if ( (record.group !== undefined || record.group == null) && (record.site !== undefined || record.site === null) ) {
|
else if ( (record.group !== undefined || record.group == null) && (record.site !== undefined || record.site === null) ) {
|
||||||
results['global'] = results['global'] || { text: 'Global', children: [] }
|
results['global'] = results['global'] || { text: 'Global', children: [] };
|
||||||
results['global'].children.push(record);
|
results['global'].children.push(record);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -246,10 +249,9 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
// Handle the null option, but only add it once
|
// Handle the null option, but only add it once
|
||||||
if (element.getAttribute('data-null-option') && data.previous === null) {
|
if (element.getAttribute('data-null-option') && data.previous === null) {
|
||||||
var null_option = $(element).children()[0];
|
|
||||||
results.unshift({
|
results.unshift({
|
||||||
id: null_option.value,
|
id: 'null',
|
||||||
text: null_option.text
|
text: 'None'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,15 +15,15 @@ router = routers.DefaultRouter()
|
|||||||
router.APIRootView = SecretsRootView
|
router.APIRootView = SecretsRootView
|
||||||
|
|
||||||
# Field choices
|
# Field choices
|
||||||
router.register(r'_choices', views.SecretsFieldChoicesViewSet, basename='field-choice')
|
router.register('_choices', views.SecretsFieldChoicesViewSet, basename='field-choice')
|
||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
router.register(r'secret-roles', views.SecretRoleViewSet)
|
router.register('secret-roles', views.SecretRoleViewSet)
|
||||||
router.register(r'secrets', views.SecretViewSet)
|
router.register('secrets', views.SecretViewSet)
|
||||||
|
|
||||||
# Miscellaneous
|
# Miscellaneous
|
||||||
router.register(r'get-session-key', views.GetSessionKeyViewSet, basename='get-session-key')
|
router.register('get-session-key', views.GetSessionKeyViewSet, basename='get-session-key')
|
||||||
router.register(r'generate-rsa-key-pair', views.GenerateRSAKeyPairViewSet, basename='generate-rsa-key-pair')
|
router.register('generate-rsa-key-pair', views.GenerateRSAKeyPairViewSet, basename='generate-rsa-key-pair')
|
||||||
|
|
||||||
app_name = 'secrets-api'
|
app_name = 'secrets-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
@ -93,8 +93,8 @@ class SecretViewSet(ModelViewSet):
|
|||||||
|
|
||||||
secret = self.get_object()
|
secret = self.get_object()
|
||||||
|
|
||||||
# Attempt to decrypt the secret if the master key is known
|
# Attempt to decrypt the secret if the user is permitted and the master key is known
|
||||||
if self.master_key is not None:
|
if secret.decryptable_by(request.user) and self.master_key is not None:
|
||||||
secret.decrypt(self.master_key)
|
secret.decrypt(self.master_key)
|
||||||
|
|
||||||
serializer = self.get_serializer(secret)
|
serializer = self.get_serializer(secret)
|
||||||
@ -111,7 +111,9 @@ class SecretViewSet(ModelViewSet):
|
|||||||
if self.master_key is not None:
|
if self.master_key is not None:
|
||||||
secrets = []
|
secrets = []
|
||||||
for secret in page:
|
for secret in page:
|
||||||
secret.decrypt(self.master_key)
|
# Enforce role permissions
|
||||||
|
if secret.decryptable_by(request.user):
|
||||||
|
secret.decrypt(self.master_key)
|
||||||
secrets.append(secret)
|
secrets.append(secret)
|
||||||
serializer = self.get_serializer(secrets, many=True)
|
serializer = self.get_serializer(secrets, many=True)
|
||||||
else:
|
else:
|
||||||
|
@ -8,8 +8,8 @@ from extras.forms import (
|
|||||||
AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelForm, CustomFieldModelCSVForm,
|
AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelForm, CustomFieldModelCSVForm,
|
||||||
)
|
)
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
APISelect, APISelectMultiple, BootstrapMixin, FilterChoiceField, FlexibleModelChoiceField, SlugField,
|
APISelect, APISelectMultiple, BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
||||||
StaticSelect2Multiple, TagFilterField
|
FlexibleModelChoiceField, SlugField, StaticSelect2Multiple, TagFilterField,
|
||||||
)
|
)
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import Secret, SecretRole, UserKey
|
from .models import Secret, SecretRole, UserKey
|
||||||
@ -87,6 +87,12 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
label='Plaintext (verify)',
|
label='Plaintext (verify)',
|
||||||
widget=forms.PasswordInput()
|
widget=forms.PasswordInput()
|
||||||
)
|
)
|
||||||
|
role = DynamicModelChoiceField(
|
||||||
|
queryset=SecretRole.objects.all(),
|
||||||
|
widget=APISelect(
|
||||||
|
api_url="/api/secrets/secret-roles/"
|
||||||
|
)
|
||||||
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
@ -96,11 +102,6 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'role', 'name', 'plaintext', 'plaintext2', 'tags',
|
'role', 'name', 'plaintext', 'plaintext2', 'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
|
||||||
'role': APISelect(
|
|
||||||
api_url="/api/secrets/secret-roles/"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -157,7 +158,7 @@ class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
queryset=Secret.objects.all(),
|
queryset=Secret.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
role = forms.ModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
queryset=SecretRole.objects.all(),
|
queryset=SecretRole.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
@ -181,9 +182,10 @@ class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
)
|
)
|
||||||
role = FilterChoiceField(
|
role = DynamicModelMultipleChoiceField(
|
||||||
queryset=SecretRole.objects.all(),
|
queryset=SecretRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
|
required=True,
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
api_url="/api/secrets/secret-roles/",
|
api_url="/api/secrets/secret-roles/",
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
|
@ -5,7 +5,8 @@ from rest_framework import status
|
|||||||
|
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||||
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
||||||
from utilities.testing import APITestCase
|
from users.models import Token
|
||||||
|
from utilities.testing import APITestCase, create_test_user
|
||||||
from .constants import PRIVATE_KEY, PUBLIC_KEY
|
from .constants import PRIVATE_KEY, PUBLIC_KEY
|
||||||
|
|
||||||
|
|
||||||
@ -131,7 +132,15 @@ class SecretTest(APITestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
super().setUp()
|
# Create a non-superuser test user
|
||||||
|
self.user = create_test_user('testuser', permissions=(
|
||||||
|
'secrets.add_secret',
|
||||||
|
'secrets.change_secret',
|
||||||
|
'secrets.delete_secret',
|
||||||
|
'secrets.view_secret',
|
||||||
|
))
|
||||||
|
self.token = Token.objects.create(user=self.user)
|
||||||
|
self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(self.token.key)}
|
||||||
|
|
||||||
userkey = UserKey(user=self.user, public_key=PUBLIC_KEY)
|
userkey = UserKey(user=self.user, public_key=PUBLIC_KEY)
|
||||||
userkey.save()
|
userkey.save()
|
||||||
@ -144,11 +153,11 @@ class SecretTest(APITestCase):
|
|||||||
'HTTP_X_SESSION_KEY': base64.b64encode(session_key.key),
|
'HTTP_X_SESSION_KEY': base64.b64encode(session_key.key),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.plaintext = {
|
self.plaintexts = (
|
||||||
'secret1': 'Secret #1 Plaintext',
|
'Secret #1 Plaintext',
|
||||||
'secret2': 'Secret #2 Plaintext',
|
'Secret #2 Plaintext',
|
||||||
'secret3': 'Secret #3 Plaintext',
|
'Secret #3 Plaintext',
|
||||||
}
|
)
|
||||||
|
|
||||||
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||||
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||||
@ -160,17 +169,17 @@ class SecretTest(APITestCase):
|
|||||||
self.secretrole1 = SecretRole.objects.create(name='Test Secret Role 1', slug='test-secret-role-1')
|
self.secretrole1 = SecretRole.objects.create(name='Test Secret Role 1', slug='test-secret-role-1')
|
||||||
self.secretrole2 = SecretRole.objects.create(name='Test Secret Role 2', slug='test-secret-role-2')
|
self.secretrole2 = SecretRole.objects.create(name='Test Secret Role 2', slug='test-secret-role-2')
|
||||||
self.secret1 = Secret(
|
self.secret1 = Secret(
|
||||||
device=self.device, role=self.secretrole1, name='Test Secret 1', plaintext=self.plaintext['secret1']
|
device=self.device, role=self.secretrole1, name='Test Secret 1', plaintext=self.plaintexts[0]
|
||||||
)
|
)
|
||||||
self.secret1.encrypt(self.master_key)
|
self.secret1.encrypt(self.master_key)
|
||||||
self.secret1.save()
|
self.secret1.save()
|
||||||
self.secret2 = Secret(
|
self.secret2 = Secret(
|
||||||
device=self.device, role=self.secretrole1, name='Test Secret 2', plaintext=self.plaintext['secret2']
|
device=self.device, role=self.secretrole1, name='Test Secret 2', plaintext=self.plaintexts[1]
|
||||||
)
|
)
|
||||||
self.secret2.encrypt(self.master_key)
|
self.secret2.encrypt(self.master_key)
|
||||||
self.secret2.save()
|
self.secret2.save()
|
||||||
self.secret3 = Secret(
|
self.secret3 = Secret(
|
||||||
device=self.device, role=self.secretrole1, name='Test Secret 3', plaintext=self.plaintext['secret3']
|
device=self.device, role=self.secretrole1, name='Test Secret 3', plaintext=self.plaintexts[2]
|
||||||
)
|
)
|
||||||
self.secret3.encrypt(self.master_key)
|
self.secret3.encrypt(self.master_key)
|
||||||
self.secret3.save()
|
self.secret3.save()
|
||||||
@ -178,16 +187,32 @@ class SecretTest(APITestCase):
|
|||||||
def test_get_secret(self):
|
def test_get_secret(self):
|
||||||
|
|
||||||
url = reverse('secrets-api:secret-detail', kwargs={'pk': self.secret1.pk})
|
url = reverse('secrets-api:secret-detail', kwargs={'pk': self.secret1.pk})
|
||||||
response = self.client.get(url, **self.header)
|
|
||||||
|
|
||||||
self.assertEqual(response.data['plaintext'], self.plaintext['secret1'])
|
# Secret plaintext not be decrypted as the user has not been assigned to the role
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertIsNone(response.data['plaintext'])
|
||||||
|
|
||||||
|
# The plaintext should be present once the user has been assigned to the role
|
||||||
|
self.secretrole1.users.add(self.user)
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertEqual(response.data['plaintext'], self.plaintexts[0])
|
||||||
|
|
||||||
def test_list_secrets(self):
|
def test_list_secrets(self):
|
||||||
|
|
||||||
url = reverse('secrets-api:secret-list')
|
url = reverse('secrets-api:secret-list')
|
||||||
response = self.client.get(url, **self.header)
|
|
||||||
|
|
||||||
|
# Secret plaintext not be decrypted as the user has not been assigned to the role
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
self.assertEqual(response.data['count'], 3)
|
self.assertEqual(response.data['count'], 3)
|
||||||
|
for secret in response.data['results']:
|
||||||
|
self.assertIsNone(secret['plaintext'])
|
||||||
|
|
||||||
|
# The plaintext should be present once the user has been assigned to the role
|
||||||
|
self.secretrole1.users.add(self.user)
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertEqual(response.data['count'], 3)
|
||||||
|
for i, secret in enumerate(response.data['results']):
|
||||||
|
self.assertEqual(secret['plaintext'], self.plaintexts[i])
|
||||||
|
|
||||||
def test_create_secret(self):
|
def test_create_secret(self):
|
||||||
|
|
||||||
|
@ -4,18 +4,13 @@ from django.urls import reverse
|
|||||||
|
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||||
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
||||||
from utilities.testing import StandardTestCases
|
from utilities.testing import ViewTestCases
|
||||||
from .constants import PRIVATE_KEY, PUBLIC_KEY
|
from .constants import PRIVATE_KEY, PUBLIC_KEY
|
||||||
|
|
||||||
|
|
||||||
class SecretRoleTestCase(StandardTestCases.Views):
|
class SecretRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = SecretRole
|
model = SecretRole
|
||||||
|
|
||||||
# Disable inapplicable tests
|
|
||||||
test_get_object = None
|
|
||||||
test_delete_object = None
|
|
||||||
test_bulk_edit_objects = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
@ -41,7 +36,7 @@ class SecretRoleTestCase(StandardTestCases.Views):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SecretTestCase(StandardTestCases.Views):
|
class SecretTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Secret
|
model = Secret
|
||||||
|
|
||||||
# Disable inapplicable tests
|
# Disable inapplicable tests
|
||||||
|
@ -8,21 +8,21 @@ app_name = 'secrets'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
# Secret roles
|
# Secret roles
|
||||||
path(r'secret-roles/', views.SecretRoleListView.as_view(), name='secretrole_list'),
|
path('secret-roles/', views.SecretRoleListView.as_view(), name='secretrole_list'),
|
||||||
path(r'secret-roles/add/', views.SecretRoleCreateView.as_view(), name='secretrole_add'),
|
path('secret-roles/add/', views.SecretRoleCreateView.as_view(), name='secretrole_add'),
|
||||||
path(r'secret-roles/import/', views.SecretRoleBulkImportView.as_view(), name='secretrole_import'),
|
path('secret-roles/import/', views.SecretRoleBulkImportView.as_view(), name='secretrole_import'),
|
||||||
path(r'secret-roles/delete/', views.SecretRoleBulkDeleteView.as_view(), name='secretrole_bulk_delete'),
|
path('secret-roles/delete/', views.SecretRoleBulkDeleteView.as_view(), name='secretrole_bulk_delete'),
|
||||||
path(r'secret-roles/<slug:slug>/edit/', views.SecretRoleEditView.as_view(), name='secretrole_edit'),
|
path('secret-roles/<slug:slug>/edit/', views.SecretRoleEditView.as_view(), name='secretrole_edit'),
|
||||||
path(r'secret-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='secretrole_changelog', kwargs={'model': SecretRole}),
|
path('secret-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='secretrole_changelog', kwargs={'model': SecretRole}),
|
||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
path(r'secrets/', views.SecretListView.as_view(), name='secret_list'),
|
path('secrets/', views.SecretListView.as_view(), name='secret_list'),
|
||||||
path(r'secrets/import/', views.SecretBulkImportView.as_view(), name='secret_import'),
|
path('secrets/import/', views.SecretBulkImportView.as_view(), name='secret_import'),
|
||||||
path(r'secrets/edit/', views.SecretBulkEditView.as_view(), name='secret_bulk_edit'),
|
path('secrets/edit/', views.SecretBulkEditView.as_view(), name='secret_bulk_edit'),
|
||||||
path(r'secrets/delete/', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
path('secrets/delete/', views.SecretBulkDeleteView.as_view(), name='secret_bulk_delete'),
|
||||||
path(r'secrets/<int:pk>/', views.SecretView.as_view(), name='secret'),
|
path('secrets/<int:pk>/', views.SecretView.as_view(), name='secret'),
|
||||||
path(r'secrets/<int:pk>/edit/', views.secret_edit, name='secret_edit'),
|
path('secrets/<int:pk>/edit/', views.secret_edit, name='secret_edit'),
|
||||||
path(r'secrets/<int:pk>/delete/', views.SecretDeleteView.as_view(), name='secret_delete'),
|
path('secrets/<int:pk>/delete/', views.SecretDeleteView.as_view(), name='secret_delete'),
|
||||||
path(r'secrets/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='secret_changelog', kwargs={'model': Secret}),
|
path('secrets/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='secret_changelog', kwargs={'model': Secret}),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -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 %}
|
|
@ -48,14 +48,30 @@
|
|||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{% if perms.dcim.add_consoleport %}<li><a href="{% url 'dcim:consoleport_add' pk=device.pk %}">Console Ports</a></li>{% endif %}
|
{% if perms.dcim.add_consoleport %}
|
||||||
{% if perms.dcim.add_consoleserverport %}<li><a href="{% url 'dcim:consoleserverport_add' pk=device.pk %}">Console Server Ports</a></li>{% endif %}
|
<li><a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Console Ports</a></li>
|
||||||
{% if perms.dcim.add_powerport %}<li><a href="{% url 'dcim:powerport_add' pk=device.pk %}">Power Ports</a></li>{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_poweroutlet %}<li><a href="{% url 'dcim:poweroutlet_add' pk=device.pk %}">Power Outlets</a></li>{% endif %}
|
{% if perms.dcim.add_consoleserverport %}
|
||||||
{% if perms.dcim.add_interface %}<li><a href="{% url 'dcim:interface_add' pk=device.pk %}">Interfaces</a></li>{% endif %}
|
<li><a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Console Server Ports</a></li>
|
||||||
{% if perms.dcim.add_frontport %}<li><a href="{% url 'dcim:frontport_add' pk=device.pk %}">Front Ports</a></li>{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_rearport %}<li><a href="{% url 'dcim:rearport_add' pk=device.pk %}">Rear Ports</a></li>{% endif %}
|
{% if perms.dcim.add_powerport %}
|
||||||
{% if perms.dcim.add_devicebay %}<li><a href="{% url 'dcim:devicebay_add' pk=device.pk %}">Device Bays</a></li>{% endif %}
|
<li><a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Power Ports</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_poweroutlet %}
|
||||||
|
<li><a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Power Outlets</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_interface %}
|
||||||
|
<li><a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Interfaces</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_frontport %}
|
||||||
|
<li><a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Front Ports</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_rearport %}
|
||||||
|
<li><a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Rear Ports</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_devicebay %}
|
||||||
|
<li><a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}">Device Bays</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -333,12 +349,12 @@
|
|||||||
{% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
|
{% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %}
|
||||||
<div class="panel-footer text-right noprint">
|
<div class="panel-footer text-right noprint">
|
||||||
{% if perms.dcim.add_consoleport %}
|
{% if perms.dcim.add_consoleport %}
|
||||||
<a href="{% url 'dcim:consoleport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
|
<a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_powerport %}
|
{% if perms.dcim.add_powerport %}
|
||||||
<a href="{% url 'dcim:powerport_add' pk=device.pk %}" class="btn btn-xs btn-primary">
|
<a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -524,13 +540,13 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device_bays and perms.dcim.delete_devicebay %}
|
{% if device_bays and perms.dcim.delete_devicebay %}
|
||||||
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_devicebay %}
|
{% if perms.dcim.add_devicebay %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'dcim:devicebay_add' pk=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -587,7 +603,7 @@
|
|||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -597,13 +613,13 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if interfaces and perms.dcim.delete_interface %}
|
{% if interfaces and perms.dcim.delete_interface %}
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_interface %}
|
{% if perms.dcim.add_interface %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'dcim:interface_add' pk=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -619,6 +635,7 @@
|
|||||||
{% if perms.dcim.delete_consoleserverport %}
|
{% if perms.dcim.delete_consoleserverport %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="device" value="{{ device.pk }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
@ -649,7 +666,7 @@
|
|||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
@ -657,13 +674,13 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if consoleserverports and perms.dcim.delete_consoleserverport %}
|
{% if consoleserverports and perms.dcim.delete_consoleserverport %}
|
||||||
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_consoleserverport %}
|
{% if perms.dcim.add_consoleserverport %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'dcim:consoleserverport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -679,6 +696,7 @@
|
|||||||
{% if perms.dcim.delete_poweroutlet %}
|
{% if perms.dcim.delete_poweroutlet %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="device" value="{{ device.pk }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
@ -710,7 +728,7 @@
|
|||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_bulk_edit' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_bulk_edit' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
@ -718,13 +736,13 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if poweroutlets and perms.dcim.delete_poweroutlet %}
|
{% if poweroutlets and perms.dcim.delete_poweroutlet %}
|
||||||
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_poweroutlet %}
|
{% if perms.dcim.add_poweroutlet %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'dcim:poweroutlet_add' pk=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -738,7 +756,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if front_ports %}
|
{% if front_ports %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="device" value="{{ device.pk }}" />
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Front Ports</strong>
|
<strong>Front Ports</strong>
|
||||||
@ -770,7 +789,7 @@
|
|||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:frontport_bulk_edit' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_edit" formaction="{% url 'dcim:frontport_bulk_edit' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:frontport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:frontport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
@ -778,13 +797,13 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if front_ports and perms.dcim.delete_frontport %}
|
{% if front_ports and perms.dcim.delete_frontport %}
|
||||||
<button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_frontport %}
|
{% if perms.dcim.add_frontport %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'dcim:frontport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add front ports
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add front ports
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -796,7 +815,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if rear_ports %}
|
{% if rear_ports %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="device" value="{{ device.pk }}" />
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Rear Ports</strong>
|
<strong>Rear Ports</strong>
|
||||||
@ -827,7 +847,7 @@
|
|||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:rearport_bulk_edit' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_edit" formaction="{% url 'dcim:rearport_bulk_edit' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:rearport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:rearport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
@ -835,13 +855,13 @@
|
|||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if rear_ports and perms.dcim.delete_rearport %}
|
{% if rear_ports and perms.dcim.delete_rearport %}
|
||||||
<button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.dcim.add_rearport %}
|
{% if perms.dcim.add_rearport %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url 'dcim:rearport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add rear ports
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add rear ports
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
{% extends '_base.html' %}
|
{% extends '_base.html' %}
|
||||||
{% load form_helpers %}
|
{% load form_helpers %}
|
||||||
|
|
||||||
{% block title %}Create {{ component_type }} ({{ parent }}){% endblock %}
|
{% block title %}Create {{ component_type }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="." method="post" class="form form-horizontal">
|
<form action="" method="post" class="form form-horizontal">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% for field in form.hidden_fields %}
|
|
||||||
{{ field }}
|
|
||||||
{% endfor %}
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="col-md-6 col-md-offset-3">
|
||||||
{% if form.non_field_errors %}
|
{% if form.non_field_errors %}
|
||||||
@ -24,12 +21,6 @@
|
|||||||
<strong>{{ component_type|title }}</strong>
|
<strong>{{ component_type|title }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-md-3 control-label required">Device</label>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<p class="form-control-static">{{ parent }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% render_form form %}
|
{% render_form form %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
{% extends '_base.html' %}
|
|
||||||
{% load buttons %}
|
|
||||||
{% load helpers %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="pull-right noprint">
|
|
||||||
{% export_button content_type %}
|
|
||||||
</div>
|
|
||||||
<h1>{% block title %}{{ table.Meta.model|model_name|capfirst }}s{% endblock %}</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-9">
|
|
||||||
{% include 'responsive_table.html' %}
|
|
||||||
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3 noprint">
|
|
||||||
{% include 'inc/search_panel.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -54,7 +54,7 @@
|
|||||||
</table>
|
</table>
|
||||||
{% if perms.dcim.add_inventoryitem %}
|
{% if perms.dcim.add_inventoryitem %}
|
||||||
<div class="panel-footer text-right noprint">
|
<div class="panel-footer text-right noprint">
|
||||||
<a href="{% url 'dcim:inventoryitem_add' device=device.pk %}" class="btn btn-primary btn-xs">
|
<a href="{% url 'dcim:inventoryitem_add' %}?device={{ device.pk }}&return_url={% url 'dcim:device_inventory' pk=device.pk %}" class="btn btn-primary btn-xs">
|
||||||
<span class="fa fa-plus" aria-hidden="true"></span> Add Inventory Item
|
<span class="fa fa-plus" aria-hidden="true"></span> Add Inventory Item
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -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,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 %}
|
|
@ -22,14 +22,14 @@
|
|||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{% if perms.dcim.add_consoleporttemplate %}<li><a href="{% url 'dcim:devicetype_add_consoleport' pk=devicetype.pk %}">Console Ports</a></li>{% endif %}
|
{% if perms.dcim.add_consoleporttemplate %}<li><a href="{% url 'dcim:consoleporttemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Console Ports</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_consoleserverporttemplate %}<li><a href="{% url 'dcim:devicetype_add_consoleserverport' pk=devicetype.pk %}">Console Server Ports</a></li>{% endif %}
|
{% if perms.dcim.add_consoleserverporttemplate %}<li><a href="{% url 'dcim:consoleserverporttemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Console Server Ports</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_powerporttemplate %}<li><a href="{% url 'dcim:devicetype_add_powerport' pk=devicetype.pk %}">Power Ports</a></li>{% endif %}
|
{% if perms.dcim.add_powerporttemplate %}<li><a href="{% url 'dcim:powerporttemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Power Ports</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_poweroutlettemplate %}<li><a href="{% url 'dcim:devicetype_add_poweroutlet' pk=devicetype.pk %}">Power Outlets</a></li>{% endif %}
|
{% if perms.dcim.add_poweroutlettemplate %}<li><a href="{% url 'dcim:poweroutlettemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Power Outlets</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_interfacetemplate %}<li><a href="{% url 'dcim:devicetype_add_interface' pk=devicetype.pk %}">Interfaces</a></li>{% endif %}
|
{% if perms.dcim.add_interfacetemplate %}<li><a href="{% url 'dcim:interfacetemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Interfaces</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_frontporttemplate %}<li><a href="{% url 'dcim:devicetype_add_frontport' pk=devicetype.pk %}">Front Ports</a></li>{% endif %}
|
{% if perms.dcim.add_frontporttemplate %}<li><a href="{% url 'dcim:frontporttemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Front Ports</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_rearporttemplate %}<li><a href="{% url 'dcim:devicetype_add_rearport' pk=devicetype.pk %}">Rear Ports</a></li>{% endif %}
|
{% if perms.dcim.add_rearporttemplate %}<li><a href="{% url 'dcim:rearporttemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Rear Ports</a></li>{% endif %}
|
||||||
{% if perms.dcim.add_devicebaytemplate %}<li><a href="{% url 'dcim:devicetype_add_devicebay' pk=devicetype.pk %}">Device Bays</a></li>{% endif %}
|
{% if perms.dcim.add_devicebaytemplate %}<li><a href="{% url 'dcim:devicebaytemplate_add' %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}">Device Bays</a></li>{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -136,48 +136,48 @@
|
|||||||
{% if devicetype.consoleport_templates.exists or devicetype.powerport_templates.exists %}
|
{% if devicetype.consoleport_templates.exists or devicetype.powerport_templates.exists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' add_url='dcim:devicetype_add_consoleport' delete_url='dcim:devicetype_delete_consoleport' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' add_url='dcim:consoleporttemplate_add' edit_url='dcim:consoleporttemplate_bulk_edit' delete_url='dcim:consoleporttemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' add_url='dcim:devicetype_add_powerport' delete_url='dcim:devicetype_delete_powerport' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' add_url='dcim:powerporttemplate_add' edit_url='dcim:powerporttemplate_bulk_edit' delete_url='dcim:powerporttemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if devicetype.is_parent_device or devicebay_table.rows %}
|
{% if devicetype.is_parent_device or devicebay_table.rows %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' add_url='dcim:devicetype_add_devicebay' delete_url='dcim:devicetype_delete_devicebay' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' add_url='dcim:devicebaytemplate_add' edit_url=None delete_url='dcim:devicebaytemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if devicetype.consoleserverport_templates.exists %}
|
{% if devicetype.consoleserverport_templates.exists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' add_url='dcim:devicetype_add_consoleserverport' delete_url='dcim:devicetype_delete_consoleserverport' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' add_url='dcim:consoleserverporttemplate_add' edit_url='dcim:consoleserverporttemplate_bulk_edit' delete_url='dcim:consoleserverporttemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if devicetype.poweroutlet_templates.exists %}
|
{% if devicetype.poweroutlet_templates.exists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' add_url='dcim:devicetype_add_poweroutlet' delete_url='dcim:devicetype_delete_poweroutlet' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' add_url='dcim:poweroutlettemplate_add' edit_url='dcim:poweroutlettemplate_bulk_edit' delete_url='dcim:poweroutlettemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if devicetype.interface_templates.exists %}
|
{% if devicetype.interface_templates.exists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' add_url='dcim:devicetype_add_interface' delete_url='dcim:devicetype_delete_interface' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' add_url='dcim:interfacetemplate_add' edit_url='dcim:interfacetemplate_bulk_edit' delete_url='dcim:interfacetemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if devicetype.frontport_templates.exists or devicetype.rearport_templates.exists %}
|
{% if devicetype.frontport_templates.exists or devicetype.rearport_templates.exists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=front_port_table title='Front Ports' add_url='dcim:devicetype_add_frontport' delete_url='dcim:devicetype_delete_frontport' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=front_port_table title='Front Ports' add_url='dcim:frontporttemplate_add' edit_url='dcim:frontporttemplate_bulk_edit' delete_url='dcim:frontporttemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=rear_port_table title='Rear Ports' add_url='dcim:devicetype_add_rearport' delete_url='dcim:devicetype_delete_rearport' %}
|
{% include 'dcim/inc/devicetype_component_table.html' with table=rear_port_table title='Rear Ports' add_url='dcim:rearporttemplate_add' edit_url='dcim:rearporttemplate_bulk_edit' delete_url='dcim:rearporttemplate_bulk_delete' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -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,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 %}
|
|
@ -9,18 +9,18 @@
|
|||||||
<div class="panel-footer noprint">
|
<div class="panel-footer noprint">
|
||||||
{% if table.rows %}
|
{% if table.rows %}
|
||||||
{% if edit_url %}
|
{% if edit_url %}
|
||||||
<button type="submit" name="_edit" formaction="{% url edit_url pk=devicetype.pk %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
<button type="submit" name="_edit" formaction="{% url edit_url %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Selected
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Selected
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if delete_url %}
|
{% if delete_url %}
|
||||||
<button type="submit" name="_delete" formaction="{% url delete_url pk=devicetype.pk %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-danger">
|
<button type="submit" name="_delete" formaction="{% url delete_url %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-danger">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url add_url pk=devicetype.pk %}{{ add_url_extra }}" class="btn btn-primary btn-xs">
|
<a href="{% url add_url %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Add {{ title }}
|
Add {{ title }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
<ul class="rack_legend">
|
|
||||||
{% for u in rack.units %}
|
|
||||||
<li>{{ u }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="rack_frame">
|
<div class="rack_frame">
|
||||||
|
|
||||||
<object data="{% url 'dcim-api:rack-elevation' pk=rack.pk %}?face={{face}}&render=svg"></object>
|
<object data="{% url 'dcim-api:rack-elevation' pk=rack.pk %}?face={{face}}&render=svg"></object>
|
||||||
|
@ -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,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,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,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 %}
|
|
@ -127,23 +127,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong>Secrets</strong>
|
|
||||||
</div>
|
|
||||||
<div class="list-group">
|
|
||||||
<div class="list-group-item">
|
|
||||||
{% if perms.secrets.view_secret %}
|
|
||||||
<span class="badge pull-right">{{ stats.secret_count }}</span>
|
|
||||||
<h4 class="list-group-item-heading"><a href="{% url 'secrets:secret_list' %}">Secrets</a></h4>
|
|
||||||
{% else %}
|
|
||||||
<span class="badge pull-right"><i class="fa fa-lock"></i></span>
|
|
||||||
<h4 class="list-group-item-heading">Secrets</h4>
|
|
||||||
{% endif %}
|
|
||||||
<p class="list-group-item-text text-muted">Cryptographically secured secret data</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-md-4">
|
<div class="col-sm-6 col-md-4">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
@ -259,6 +242,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-md-4">
|
<div class="col-sm-6 col-md-4">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Secrets</strong>
|
||||||
|
</div>
|
||||||
|
<div class="list-group">
|
||||||
|
<div class="list-group-item">
|
||||||
|
{% if perms.secrets.view_secret %}
|
||||||
|
<span class="badge pull-right">{{ stats.secret_count }}</span>
|
||||||
|
<h4 class="list-group-item-heading"><a href="{% url 'secrets:secret_list' %}">Secrets</a></h4>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge pull-right"><i class="fa fa-lock"></i></span>
|
||||||
|
<h4 class="list-group-item-heading">Secrets</h4>
|
||||||
|
{% endif %}
|
||||||
|
<p class="list-group-item-text text-muted">Cryptographically secured secret data</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Reports</strong>
|
<strong>Reports</strong>
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ field }}</td>
|
<td>{{ field }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if field.type == 300 and value == True %}
|
{% if field.type == 'boolean' and value == True %}
|
||||||
<i class="glyphicon glyphicon-ok text-success" title="True"></i>
|
<i class="glyphicon glyphicon-ok text-success" title="True"></i>
|
||||||
{% elif field.type == 300 and value == False %}
|
{% elif field.type == 'boolean' and value == False %}
|
||||||
<i class="glyphicon glyphicon-remove text-danger" title="False"></i>
|
<i class="glyphicon glyphicon-remove text-danger" title="False"></i>
|
||||||
{% elif field.type == 500 and value %}
|
{% elif field.type == 'url' and value %}
|
||||||
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
<a href="{{ value }}">{{ value|truncatechars:70 }}</a>
|
||||||
{% elif field.type == 200 or value %}
|
{% elif field.type == 'integer' or value %}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
{% elif field.required %}
|
{% elif field.required %}
|
||||||
<span class="text-warning">Not defined</span>
|
<span class="text-warning">Not defined</span>
|
||||||
|
@ -239,7 +239,7 @@
|
|||||||
<a href="{% url 'dcim:poweroutlet_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
<a href="{% url 'dcim:poweroutlet_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% url 'dcim:poweroutlet_list' %}">Power Outlet</a>
|
<a href="{% url 'dcim:poweroutlet_list' %}">Power Outlets</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% if not perms.dcim.view_devicebay %} class="disabled"{% endif %}>
|
<li{% if not perms.dcim.view_devicebay %} class="disabled"{% endif %}>
|
||||||
{% if perms.dcim.add_devicebay %}
|
{% if perms.dcim.add_devicebay %}
|
||||||
@ -473,11 +473,16 @@
|
|||||||
<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>
|
||||||
<li{% if not perms.extras.view_configcontext %} class="disabled"{% endif %}>
|
<li{% if not perms.extras.view_configcontext %} class="disabled"{% endif %}>
|
||||||
|
{% if perms.extras.add_configcontext %}
|
||||||
|
<div class="buttons pull-right">
|
||||||
|
<a href="{% url 'extras:configcontext_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<a href="{% url 'extras:configcontext_list' %}">Config Contexts</a>
|
<a href="{% url 'extras:configcontext_list' %}">Config Contexts</a>
|
||||||
</li>
|
</li>
|
||||||
<li{% if not perms.extras.view_script %} class="disabled"{% endif %}>
|
<li{% if not perms.extras.view_script %} class="disabled"{% endif %}>
|
||||||
|
@ -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 %}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user