Compare commits

..

2 Commits

Author SHA1 Message Date
Arthur
18efe72b11 14884 log error on form validation fail 2025-10-15 16:24:37 -07:00
Arthur
6930df85f3 14884 de-reference script params from EventRule 2025-10-15 16:16:40 -07:00
247 changed files with 4389 additions and 17733 deletions

View File

@@ -15,7 +15,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v4.4.4
placeholder: v4.4.3
validations:
required: true
- type: dropdown

View File

@@ -27,7 +27,7 @@ body:
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v4.4.4
placeholder: v4.4.3
validations:
required: true
- type: dropdown

View File

@@ -166,8 +166,7 @@ strawberry-graphql-django
svgwrite
# Tabular dataset library (for table-based exports)
# Current: https://github.com/jazzband/tablib/releases
# Previous: https://github.com/jazzband/tablib/blob/master/HISTORY.md
# https://github.com/jazzband/tablib/blob/master/HISTORY.md
tablib
# Timezone data (required by django-timezone-field on Python 3.9+)

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,5 @@
# GraphQL API Parameters
## GRAPHQL_DEFAULT_VERSION
!!! note "This parameter was introduced in NetBox v4.5."
Default: `1`
Designates the default version of the GraphQL API served by `/graphql/`. To access a specific version, append the version number to the URL, e.g. `/graphql/v2/`.
---
## GRAPHQL_ENABLED
!!! tip "Dynamic Configuration Parameter"

View File

@@ -90,10 +90,3 @@ http://netbox:8000/api/extras/config-templates/123/render/ \
"bar": 123
}'
```
!!! note "Permissions"
Rendering configuration templates via the REST API requires appropriate permissions for the relevant object type:
* To render a device's configuration via `/api/dcim/devices/{id}/render-config/`, assign a permission for "DCIM > Device" with the `render_config` action.
* To render a virtual machine's configuration via `/api/virtualization/virtual-machines/{id}/render-config/`, assign a permission for "Virtualization > Virtual Machine" with the `render_config` action.
* To render a config template directly via `/api/extras/config-templates/{id}/render/`, assign a permission for "Extras > Config Template" with the `render` action.

View File

@@ -1,10 +0,0 @@
# Resource Ownership
!!! info "This feature was introduced in NetBox v4.5."
Most objects in NetBox can be assigned an owner. An owner is a set of users and/or groups who are responsible for the administration of associated objects. For example, you might designate the operations team at a site as the owner for all prefixes and VLANs deployed at that site. The users and groups assigned to an owner are referred to as its members.
!!! note
Ownership of an object should not be confused with the concept of [tenancy](./tenancy.md), which indicates the dedication of an object to a specific tenant. For instance, a tenant might represent a customer served by the object, whereas an owner typically represents a set of internal users responsible for the management of the object.
Owners can be organized into groups for easier management.

View File

@@ -1,6 +1,6 @@
# Tenancy
Most core objects within NetBox's data model support _tenancy_. This is the association of an object with a particular tenant to convey assignment or dependency. For example, an enterprise might represent its internal business units as tenants, whereas a managed services provider might create a tenant in NetBox to represent each of its customers.
Most core objects within NetBox's data model support _tenancy_. This is the association of an object with a particular tenant to convey ownership or dependency. For example, an enterprise might represent its internal business units as tenants, whereas a managed services provider might create a tenant in NetBox to represent each of its customers.
```mermaid
flowchart TD
@@ -19,36 +19,20 @@ Tenants can be grouped by any logic that your use case demands, and groups can b
Typically, the tenant model is used to represent a customer or internal organization, however it can be used for whatever purpose meets your needs.
Most core objects within NetBox can be assigned to a particular tenant, so this model provides a very convenient way to correlate resource allocation across object types. For example, each of your customers might have its own racks, devices, IP addresses, circuits and so on: These can all be easily tracked via tenant assignment.
Most core objects within NetBox can be assigned to particular tenant, so this model provides a very convenient way to correlate ownership across object types. For example, each of your customers might have its own racks, devices, IP addresses, circuits and so on: These can all be easily tracked via tenant assignment.
The following objects can be assigned to tenants:
* Circuits
* Circuit groups
* Virtual circuits
* Cables
* Devices
* Virtual device contexts
* Power feeds
* Sites
* Racks
* Rack reservations
* Sites
* Locations
* ASNs
* ASN ranges
* Aggregates
* Devices
* VRFs
* Prefixes
* IP ranges
* IP addresses
* VLANs
* VLAN groups
* VRFs
* Route targets
* Circuits
* Clusters
* Virtual machines
* L2VPNs
* Tunnels
* Wireless LANs
* Wireless links
Tenancy represents the dedication of an object to a specific tenant. As such, each object may only be assigned to a single tenant. For example, if you have a firewall dedicated to a particular customer, you would assign it to the tenant which represents that customer. However, if the firewall serves multiple customers, it doesn't *belong* to any particular customer, so the assignment of a tenant would not be appropriate.
Tenant assignment is used to signify the ownership of an object in NetBox. As such, each object may only be owned by a single tenant. For example, if you have a firewall dedicated to a particular customer, you would assign it to the tenant which represents that customer. However, if the firewall serves multiple customers, it doesn't *belong* to any particular customer, so tenant assignment would not be appropriate.

View File

@@ -1,23 +0,0 @@
# Owner
An owner is a set of users and/or groups who are responsible for the administration of certain resources within NetBox. The users and groups assigned to an owner are referred to as its members. Owner assignments are useful for indicating which parties are responsible for the administration of a particular object.
Most objects within NetBox can be assigned an owner, although this is not required.
## Fields
### Name
The owner's name.
### Group
The [group](./ownergroup.md) to which the owner is assigned. The assignment of an owner to a group is optional.
### User Groups
Groups of users that are members of the owner.
### Users
Individual users that are members of the owner.

View File

@@ -1,9 +0,0 @@
# Owner Groups
Groups are used to correlate and organize [owners](./owner.md). The assignment of an owner to a group has no bearing on the relationship of owned objects to their owners.
## Fields
### Name
The name of the group.

View File

@@ -1,17 +1,5 @@
# NetBox v4.4
## v4.4.4 (2025-10-15)
### Bug Fixes
* [#20554](https://github.com/netbox-community/netbox/issues/20554) - Fix generic relation filters to accept `<app>.<model>` format matching POST requests
* [#20574](https://github.com/netbox-community/netbox/issues/20574) - Fix excessive storage initialization overhead when listing scripts with remote backends
* [#20584](https://github.com/netbox-community/netbox/issues/20584) - Enforce PoE mode requirement on interface templates when PoE type is set
* [#20585](https://github.com/netbox-community/netbox/issues/20585) - Fix API schema generation crash for models with single-field UniqueConstraints
* [#20587](https://github.com/netbox-community/netbox/issues/20587) - Fix upgrade.sh failure when removing stale content types
---
## v4.4.3 (2025-10-14)
### Enhancements

View File

@@ -77,7 +77,6 @@ nav:
- Wireless: 'features/wireless.md'
- Virtualization: 'features/virtualization.md'
- VPN Tunnels: 'features/vpn-tunnels.md'
- Resource Ownership: 'features/resource-ownership.md'
- Tenancy: 'features/tenancy.md'
- Contacts: 'features/contacts.md'
- Search: 'features/search.md'
@@ -274,9 +273,6 @@ nav:
- ContactRole: 'models/tenancy/contactrole.md'
- Tenant: 'models/tenancy/tenant.md'
- TenantGroup: 'models/tenancy/tenantgroup.md'
- Users:
- Owner: 'models/users/owner.md'
- OwnerGroup: 'models/users/ownergroup.md'
- Virtualization:
- Cluster: 'models/virtualization/cluster.md'
- ClusterGroup: 'models/virtualization/clustergroup.md'

View File

@@ -11,9 +11,7 @@ from circuits.models import (
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.api.serializers_.cables import CabledObjectSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import (
NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer, WritableNestedSerializer,
)
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from netbox.choices import DistanceUnitChoices
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
@@ -31,7 +29,7 @@ __all__ = (
)
class CircuitTypeSerializer(OrganizationalModelSerializer):
class CircuitTypeSerializer(NetBoxModelSerializer):
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
@@ -39,8 +37,8 @@ class CircuitTypeSerializer(OrganizationalModelSerializer):
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
@@ -73,15 +71,15 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
return serializer(obj.termination, nested=True, context=context).data
class CircuitGroupSerializer(OrganizationalModelSerializer):
class CircuitGroupSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
circuit_count = RelatedObjectCountField('assignments')
class Meta:
model = CircuitGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count'
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant',
'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count'
]
brief_fields = ('id', 'url', 'display', 'name')
@@ -101,7 +99,7 @@ class CircuitGroupAssignmentSerializer_(NetBoxModelSerializer):
brief_fields = ('id', 'url', 'display', 'group', 'priority')
class CircuitSerializer(PrimaryModelSerializer):
class CircuitSerializer(NetBoxModelSerializer):
provider = ProviderSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
@@ -117,7 +115,7 @@ class CircuitSerializer(PrimaryModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant',
'install_date', 'termination_date', 'commit_rate', 'description', 'distance', 'distance_unit',
'termination_a', 'termination_z', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'termination_a', 'termination_z', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'assignments',
]
brief_fields = ('id', 'url', 'display', 'provider', 'cid', 'description')
@@ -178,7 +176,7 @@ class CircuitGroupAssignmentSerializer(CircuitGroupAssignmentSerializer_):
return serializer(obj.member, nested=True, context=context).data
class VirtualCircuitTypeSerializer(OrganizationalModelSerializer):
class VirtualCircuitTypeSerializer(NetBoxModelSerializer):
# Related object counts
virtual_circuit_count = RelatedObjectCountField('virtual_circuits')
@@ -186,13 +184,13 @@ class VirtualCircuitTypeSerializer(OrganizationalModelSerializer):
class Meta:
model = VirtualCircuitType
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'virtual_circuit_count',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'virtual_circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'virtual_circuit_count')
class VirtualCircuitSerializer(PrimaryModelSerializer):
class VirtualCircuitSerializer(NetBoxModelSerializer):
provider_network = ProviderNetworkSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
type = VirtualCircuitTypeSerializer(nested=True)
@@ -203,7 +201,7 @@ class VirtualCircuitSerializer(PrimaryModelSerializer):
model = VirtualCircuit
fields = [
'id', 'url', 'display_url', 'display', 'cid', 'provider_network', 'provider_account', 'type', 'status',
'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'tenant', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'provider_network', 'cid', 'description')

View File

@@ -4,7 +4,7 @@ from circuits.models import Provider, ProviderAccount, ProviderNetwork
from ipam.api.serializers_.asns import ASNSerializer
from ipam.models import ASN
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from .nested import NestedProviderAccountSerializer
__all__ = (
@@ -14,7 +14,7 @@ __all__ = (
)
class ProviderSerializer(PrimaryModelSerializer):
class ProviderSerializer(NetBoxModelSerializer):
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
@@ -35,32 +35,32 @@ class ProviderSerializer(PrimaryModelSerializer):
class Meta:
model = Provider
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'accounts', 'description', 'owner', 'comments',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'accounts', 'description', 'comments',
'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
class ProviderAccountSerializer(PrimaryModelSerializer):
class ProviderAccountSerializer(NetBoxModelSerializer):
provider = ProviderSerializer(nested=True)
name = serializers.CharField(allow_blank=True, max_length=100, required=False, default='')
class Meta:
model = ProviderAccount
fields = [
'id', 'url', 'display_url', 'display', 'provider', 'name', 'account', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
class ProviderNetworkSerializer(PrimaryModelSerializer):
class ProviderNetworkSerializer(NetBoxModelSerializer):
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderNetwork
fields = [
'id', 'url', 'display_url', 'display', 'provider', 'name', 'service_id', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -6,7 +6,7 @@ from django.utils.translation import gettext as _
from dcim.filtersets import CabledObjectFilterSet
from dcim.models import Interface, Location, Region, Site, SiteGroup
from ipam.models import ASN
from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, TreeNodeMultipleChoiceFilter,
@@ -29,7 +29,7 @@ __all__ = (
)
class ProviderFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations___region',
@@ -95,7 +95,7 @@ class ProviderFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
)
class ProviderAccountFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
class ProviderAccountFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
@@ -122,7 +122,7 @@ class ProviderAccountFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
).distinct()
class ProviderNetworkFilterSet(PrimaryModelFilterSet):
class ProviderNetworkFilterSet(NetBoxModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
@@ -156,7 +156,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
class CircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
@@ -475,7 +475,7 @@ class VirtualCircuitTypeFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
class VirtualCircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
class VirtualCircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
field_name='provider_network__provider',
queryset=Provider.objects.all(),

View File

@@ -11,11 +11,11 @@ from circuits.models import *
from dcim.models import Site
from ipam.models import ASN
from netbox.choices import DistanceUnitChoices
from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice, get_field_value
from utilities.forms.fields import (
ColorField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
ColorField, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, HTMXSelect, NumberWithOptions
@@ -36,12 +36,18 @@ __all__ = (
)
class ProviderBulkEditForm(PrimaryModelBulkEditForm):
class ProviderBulkEditForm(NetBoxModelBulkEditForm):
asns = DynamicModelMultipleChoiceField(
queryset=ASN.objects.all(),
label=_('ASNs'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Provider
fieldsets = (
@@ -52,12 +58,18 @@ class ProviderBulkEditForm(PrimaryModelBulkEditForm):
)
class ProviderAccountBulkEditForm(PrimaryModelBulkEditForm):
class ProviderAccountBulkEditForm(NetBoxModelBulkEditForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = ProviderAccount
fieldsets = (
@@ -68,7 +80,7 @@ class ProviderAccountBulkEditForm(PrimaryModelBulkEditForm):
)
class ProviderNetworkBulkEditForm(PrimaryModelBulkEditForm):
class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -79,6 +91,12 @@ class ProviderNetworkBulkEditForm(PrimaryModelBulkEditForm):
required=False,
label=_('Service ID')
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = ProviderNetwork
fieldsets = (
@@ -89,11 +107,16 @@ class ProviderNetworkBulkEditForm(PrimaryModelBulkEditForm):
)
class CircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
model = CircuitType
fieldsets = (
@@ -102,7 +125,7 @@ class CircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
nullable_fields = ('color', 'description')
class CircuitBulkEditForm(PrimaryModelBulkEditForm):
class CircuitBulkEditForm(NetBoxModelBulkEditForm):
type = DynamicModelChoiceField(
label=_('Type'),
queryset=CircuitType.objects.all(),
@@ -160,6 +183,12 @@ class CircuitBulkEditForm(PrimaryModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=100,
required=False
)
comments = CommentField()
model = Circuit
fieldsets = (
@@ -232,7 +261,12 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm):
pass
class CircuitGroupBulkEditForm(OrganizationalModelBulkEditForm):
class CircuitGroupBulkEditForm(NetBoxModelBulkEditForm):
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
tenant = DynamicModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -264,11 +298,16 @@ class CircuitGroupAssignmentBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('priority',)
class VirtualCircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
class VirtualCircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
model = VirtualCircuitType
fieldsets = (
@@ -277,7 +316,7 @@ class VirtualCircuitTypeBulkEditForm(OrganizationalModelBulkEditForm):
nullable_fields = ('color', 'description')
class VirtualCircuitBulkEditForm(PrimaryModelBulkEditForm):
class VirtualCircuitBulkEditForm(NetBoxModelBulkEditForm):
provider_network = DynamicModelChoiceField(
label=_('Provider network'),
queryset=ProviderNetwork.objects.all(),
@@ -304,6 +343,12 @@ class VirtualCircuitBulkEditForm(PrimaryModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=100,
required=False
)
comments = CommentField()
model = VirtualCircuit
fieldsets = (

View File

@@ -7,7 +7,7 @@ from circuits.constants import *
from circuits.models import *
from dcim.models import Interface
from netbox.choices import DistanceUnitChoices
from netbox.forms import NetBoxModelImportForm, OrganizationalModelImportForm, PrimaryModelImportForm
from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
@@ -28,17 +28,17 @@ __all__ = (
)
class ProviderImportForm(PrimaryModelImportForm):
class ProviderImportForm(NetBoxModelImportForm):
slug = SlugField()
class Meta:
model = Provider
fields = (
'name', 'slug', 'description', 'owner', 'comments', 'tags',
'name', 'slug', 'description', 'comments', 'tags',
)
class ProviderAccountImportForm(PrimaryModelImportForm):
class ProviderAccountImportForm(NetBoxModelImportForm):
provider = CSVModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -49,11 +49,11 @@ class ProviderAccountImportForm(PrimaryModelImportForm):
class Meta:
model = ProviderAccount
fields = (
'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
'provider', 'name', 'account', 'description', 'comments', 'tags',
)
class ProviderNetworkImportForm(PrimaryModelImportForm):
class ProviderNetworkImportForm(NetBoxModelImportForm):
provider = CSVModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -64,19 +64,19 @@ class ProviderNetworkImportForm(PrimaryModelImportForm):
class Meta:
model = ProviderNetwork
fields = [
'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags'
'provider', 'name', 'service_id', 'description', 'comments', 'tags'
]
class CircuitTypeImportForm(OrganizationalModelImportForm):
class CircuitTypeImportForm(NetBoxModelImportForm):
slug = SlugField()
class Meta:
model = CircuitType
fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
fields = ('name', 'slug', 'color', 'description', 'tags')
class CircuitImportForm(PrimaryModelImportForm):
class CircuitImportForm(NetBoxModelImportForm):
provider = CSVModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -119,7 +119,7 @@ class CircuitImportForm(PrimaryModelImportForm):
model = Circuit
fields = [
'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date', 'termination_date',
'commit_rate', 'distance', 'distance_unit', 'description', 'owner', 'comments', 'tags'
'commit_rate', 'distance', 'distance_unit', 'description', 'comments', 'tags'
]
@@ -165,7 +165,7 @@ class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTermination
}
class CircuitGroupImportForm(OrganizationalModelImportForm):
class CircuitGroupImportForm(NetBoxModelImportForm):
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -176,7 +176,7 @@ class CircuitGroupImportForm(OrganizationalModelImportForm):
class Meta:
model = CircuitGroup
fields = ('name', 'slug', 'description', 'tenant', 'owner', 'tags')
fields = ('name', 'slug', 'description', 'tenant', 'tags')
class CircuitGroupAssignmentImportForm(NetBoxModelImportForm):
@@ -195,14 +195,15 @@ class CircuitGroupAssignmentImportForm(NetBoxModelImportForm):
fields = ('member_type', 'member_id', 'group', 'priority')
class VirtualCircuitTypeImportForm(OrganizationalModelImportForm):
class VirtualCircuitTypeImportForm(NetBoxModelImportForm):
slug = SlugField()
class Meta:
model = VirtualCircuitType
fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
fields = ('name', 'slug', 'color', 'description', 'tags')
class VirtualCircuitImportForm(PrimaryModelImportForm):
class VirtualCircuitImportForm(NetBoxModelImportForm):
provider_network = CSVModelChoiceField(
label=_('Provider network'),
queryset=ProviderNetwork.objects.all(),
@@ -238,8 +239,8 @@ class VirtualCircuitImportForm(PrimaryModelImportForm):
class Meta:
model = VirtualCircuit
fields = [
'cid', 'provider_network', 'provider_account', 'type', 'status', 'tenant', 'description', 'owner',
'comments', 'tags',
'cid', 'provider_network', 'provider_account', 'type', 'status', 'tenant', 'description', 'comments',
'tags',
]

View File

@@ -9,7 +9,7 @@ from circuits.models import *
from dcim.models import Location, Region, Site, SiteGroup
from ipam.models import ASN
from netbox.choices import DistanceUnitChoices
from netbox.forms import NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm, PrimaryModelFilterSetForm
from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
@@ -31,10 +31,10 @@ __all__ = (
)
class ProviderFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Provider
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('asn_id', name=_('ASN')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -66,10 +66,10 @@ class ProviderFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
tag = TagFilterField(model)
class ProviderAccountFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
class ProviderAccountFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = ProviderAccount
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'account', name=_('Attributes')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
@@ -85,10 +85,10 @@ class ProviderAccountFilterForm(ContactModelFilterForm, PrimaryModelFilterSetFor
tag = TagFilterField(model)
class ProviderNetworkFilterForm(PrimaryModelFilterSetForm):
class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
model = ProviderNetwork
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'service_id', name=_('Attributes')),
)
provider_id = DynamicModelMultipleChoiceField(
@@ -104,10 +104,10 @@ class ProviderNetworkFilterForm(PrimaryModelFilterSetForm):
tag = TagFilterField(model)
class CircuitTypeFilterForm(OrganizationalModelFilterSetForm):
class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
model = CircuitType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('color', name=_('Attributes')),
)
tag = TagFilterField(model)
@@ -118,10 +118,10 @@ class CircuitTypeFilterForm(OrganizationalModelFilterSetForm):
)
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Circuit
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet(
'type_id', 'status', 'install_date', 'termination_date', 'commit_rate', 'distance', 'distance_unit',
@@ -271,10 +271,10 @@ class CircuitTerminationFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
class CircuitGroupFilterForm(TenancyFilterForm, OrganizationalModelFilterSetForm):
class CircuitGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = CircuitGroup
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
tag = TagFilterField(model)
@@ -309,10 +309,10 @@ class CircuitGroupAssignmentFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
class VirtualCircuitTypeFilterForm(OrganizationalModelFilterSetForm):
class VirtualCircuitTypeFilterForm(NetBoxModelFilterSetForm):
model = VirtualCircuitType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('color', name=_('Attributes')),
)
tag = TagFilterField(model)
@@ -323,10 +323,10 @@ class VirtualCircuitTypeFilterForm(OrganizationalModelFilterSetForm):
)
class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = VirtualCircuit
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet('type_id', 'status', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),

View File

@@ -10,11 +10,11 @@ from circuits.constants import *
from circuits.models import *
from dcim.models import Interface, Site
from ipam.models import ASN
from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm
from utilities.forms import get_field_value
from utilities.forms.fields import (
ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
)
from utilities.forms.mixins import DistanceValidationMixin
from utilities.forms.rendering import FieldSet, InlineFields
@@ -36,13 +36,14 @@ __all__ = (
)
class ProviderForm(PrimaryModelForm):
class ProviderForm(NetBoxModelForm):
slug = SlugField()
asns = DynamicModelMultipleChoiceField(
queryset=ASN.objects.all(),
label=_('ASNs'),
required=False
)
comments = CommentField()
fieldsets = (
FieldSet('name', 'slug', 'asns', 'description', 'tags'),
@@ -51,32 +52,34 @@ class ProviderForm(PrimaryModelForm):
class Meta:
model = Provider
fields = [
'name', 'slug', 'asns', 'description', 'owner', 'comments', 'tags',
'name', 'slug', 'asns', 'description', 'comments', 'tags',
]
class ProviderAccountForm(PrimaryModelForm):
class ProviderAccountForm(NetBoxModelForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
selector=True,
quick_add=True
)
comments = CommentField()
class Meta:
model = ProviderAccount
fields = [
'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
'provider', 'name', 'account', 'description', 'comments', 'tags',
]
class ProviderNetworkForm(PrimaryModelForm):
class ProviderNetworkForm(NetBoxModelForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
selector=True,
quick_add=True
)
comments = CommentField()
fieldsets = (
FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
@@ -85,13 +88,15 @@ class ProviderNetworkForm(PrimaryModelForm):
class Meta:
model = ProviderNetwork
fields = [
'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags',
'provider', 'name', 'service_id', 'description', 'comments', 'tags',
]
class CircuitTypeForm(OrganizationalModelForm):
class CircuitTypeForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'owner', 'tags'),
FieldSet('name', 'slug', 'color', 'description', 'tags'),
)
class Meta:
@@ -101,7 +106,7 @@ class CircuitTypeForm(OrganizationalModelForm):
]
class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
provider = DynamicModelChoiceField(
label=_('Provider'),
queryset=Provider.objects.all(),
@@ -120,6 +125,7 @@ class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
queryset=CircuitType.objects.all(),
quick_add=True
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -141,7 +147,7 @@ class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
model = Circuit
fields = [
'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'comments', 'tags',
]
widgets = {
'install_date': DatePicker(),
@@ -227,7 +233,9 @@ class CircuitTerminationForm(NetBoxModelForm):
self.instance.termination = self.cleaned_data.get('termination')
class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
class CircuitGroupForm(TenancyForm, NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
@@ -236,7 +244,7 @@ class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
class Meta:
model = CircuitGroup
fields = [
'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'tags',
'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
]
@@ -299,7 +307,9 @@ class CircuitGroupAssignmentForm(NetBoxModelForm):
self.instance.member = self.cleaned_data.get('member')
class VirtualCircuitTypeForm(OrganizationalModelForm):
class VirtualCircuitTypeForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags'),
)
@@ -307,11 +317,11 @@ class VirtualCircuitTypeForm(OrganizationalModelForm):
class Meta:
model = VirtualCircuitType
fields = [
'name', 'slug', 'color', 'description', 'owner', 'tags',
'name', 'slug', 'color', 'description', 'tags',
]
class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
provider_network = DynamicModelChoiceField(
label=_('Provider network'),
queryset=ProviderNetwork.objects.all(),
@@ -326,6 +336,7 @@ class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
queryset=VirtualCircuitType.objects.all(),
quick_add=True
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -339,7 +350,7 @@ class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
model = VirtualCircuit
fields = [
'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
'owner', 'comments', 'tags',
'comments', 'tags',
]

View File

@@ -6,7 +6,7 @@ import strawberry_django
from circuits import models
from dcim.graphql.mixins import CabledObjectMixin
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
from netbox.graphql.types import BaseObjectType, ObjectType, OrganizationalObjectType, PrimaryObjectType
from netbox.graphql.types import BaseObjectType, NetBoxObjectType, ObjectType, OrganizationalObjectType
from tenancy.graphql.types import TenantType
from .filters import *
@@ -35,7 +35,8 @@ __all__ = (
filters=ProviderFilter,
pagination=True
)
class ProviderType(ContactsMixin, PrimaryObjectType):
class ProviderType(NetBoxObjectType, ContactsMixin):
networks: List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
@@ -48,8 +49,9 @@ class ProviderType(ContactsMixin, PrimaryObjectType):
filters=ProviderAccountFilter,
pagination=True
)
class ProviderAccountType(ContactsMixin, PrimaryObjectType):
class ProviderAccountType(ContactsMixin, NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
@@ -59,8 +61,9 @@ class ProviderAccountType(ContactsMixin, PrimaryObjectType):
filters=ProviderNetworkFilter,
pagination=True
)
class ProviderNetworkType(PrimaryObjectType):
class ProviderNetworkType(NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
circuit_terminations: List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
@@ -102,13 +105,14 @@ class CircuitTypeType(OrganizationalObjectType):
filters=CircuitFilter,
pagination=True
)
class CircuitType(PrimaryObjectType, ContactsMixin):
class CircuitType(NetBoxObjectType, ContactsMixin):
provider: ProviderType
provider_account: ProviderAccountType | None
termination_a: CircuitTerminationType | None
termination_z: CircuitTerminationType | None
type: CircuitTypeType
tenant: TenantType | None
terminations: List[CircuitTerminationType]
@@ -174,11 +178,12 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
filters=VirtualCircuitFilter,
pagination=True
)
class VirtualCircuitType(PrimaryObjectType):
class VirtualCircuitType(NetBoxObjectType):
provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"])
provider_account: ProviderAccountType | None
type: Annotated["VirtualCircuitTypeType", strawberry.lazy('circuits.graphql.types')] = strawberry_django.field(
select_related=["type"]
)
tenant: TenantType | None
terminations: List[VirtualCircuitTerminationType]

View File

@@ -1,68 +0,0 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0052_extend_circuit_abs_distance_upper_limit'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='circuit',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='circuitgroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='circuittype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='provider',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='provideraccount',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='providernetwork',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='virtualcircuit',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='virtualcircuittype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
]

View File

@@ -1,9 +1,11 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from circuits.models import *
from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from netbox.tables import NetBoxTable, columns
from .columns import CommitRateColumn
__all__ = (
@@ -22,7 +24,7 @@ CIRCUITTERMINATION_LINK = """
"""
class CircuitTypeTable(OrganizationalModelTable):
class CircuitTypeTable(NetBoxTable):
name = tables.Column(
linkify=True,
verbose_name=_('Name'),
@@ -37,7 +39,7 @@ class CircuitTypeTable(OrganizationalModelTable):
verbose_name=_('Circuits')
)
class Meta(OrganizationalModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = CircuitType
fields = (
'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'tags', 'created', 'last_updated',
@@ -46,7 +48,7 @@ class CircuitTypeTable(OrganizationalModelTable):
default_columns = ('pk', 'name', 'circuit_count', 'color', 'description')
class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
cid = tables.Column(
linkify=True,
verbose_name=_('Circuit ID')
@@ -77,6 +79,9 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
verbose_name=_('Commit Rate')
)
distance = columns.DistanceColumn()
comments = columns.MarkdownColumn(
verbose_name=_('Comments')
)
tags = columns.TagColumn(
url_name='circuits:circuit_list'
)
@@ -85,7 +90,7 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
linkify_item=True
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Circuit
fields = (
'pk', 'id', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'tenant_group',
@@ -158,7 +163,7 @@ class CircuitTerminationTable(NetBoxTable):
)
class CircuitGroupTable(OrganizationalModelTable):
class CircuitGroupTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -172,7 +177,7 @@ class CircuitGroupTable(OrganizationalModelTable):
url_name='circuits:circuitgroup_list'
)
class Meta(OrganizationalModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = CircuitGroup
fields = (
'pk', 'name', 'description', 'circuit_group_assignment_count', 'tags',

View File

@@ -1,11 +1,11 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
from circuits.models import *
from netbox.tables import PrimaryModelTable, columns
from django_tables2.utils import Accessor
from tenancy.tables import ContactsColumnMixin
from netbox.tables import NetBoxTable, columns
__all__ = (
'ProviderTable',
'ProviderAccountTable',
@@ -13,7 +13,7 @@ __all__ = (
)
class ProviderTable(ContactsColumnMixin, PrimaryModelTable):
class ProviderTable(ContactsColumnMixin, NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -42,11 +42,14 @@ class ProviderTable(ContactsColumnMixin, PrimaryModelTable):
url_params={'provider_id': 'pk'},
verbose_name=_('Circuits')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='circuits:provider_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Provider
fields = (
'pk', 'id', 'name', 'accounts', 'account_count', 'asns', 'asn_count', 'circuit_count', 'description',
@@ -55,7 +58,7 @@ class ProviderTable(ContactsColumnMixin, PrimaryModelTable):
default_columns = ('pk', 'name', 'account_count', 'circuit_count')
class ProviderAccountTable(ContactsColumnMixin, PrimaryModelTable):
class ProviderAccountTable(ContactsColumnMixin, NetBoxTable):
account = tables.Column(
linkify=True,
verbose_name=_('Account'),
@@ -73,11 +76,14 @@ class ProviderAccountTable(ContactsColumnMixin, PrimaryModelTable):
url_params={'provider_account_id': 'pk'},
verbose_name=_('Circuits')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='circuits:provideraccount_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = ProviderAccount
fields = (
'pk', 'id', 'account', 'name', 'provider', 'circuit_count', 'comments', 'contacts', 'tags', 'created',
@@ -86,7 +92,7 @@ class ProviderAccountTable(ContactsColumnMixin, PrimaryModelTable):
default_columns = ('pk', 'account', 'name', 'provider', 'circuit_count')
class ProviderNetworkTable(PrimaryModelTable):
class ProviderNetworkTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -95,11 +101,14 @@ class ProviderNetworkTable(PrimaryModelTable):
verbose_name=_('Provider'),
linkify=True
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='circuits:providernetwork_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = ProviderNetwork
fields = (
'pk', 'id', 'name', 'provider', 'service_id', 'description', 'comments', 'created', 'last_updated', 'tags',

View File

@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from circuits.models import *
from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
__all__ = (
@@ -12,7 +12,7 @@ __all__ = (
)
class VirtualCircuitTypeTable(OrganizationalModelTable):
class VirtualCircuitTypeTable(NetBoxTable):
name = tables.Column(
linkify=True,
verbose_name=_('Name'),
@@ -27,7 +27,7 @@ class VirtualCircuitTypeTable(OrganizationalModelTable):
verbose_name=_('Circuits')
)
class Meta(OrganizationalModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = VirtualCircuitType
fields = (
'pk', 'id', 'name', 'virtual_circuit_count', 'color', 'description', 'slug', 'tags', 'created',
@@ -36,7 +36,7 @@ class VirtualCircuitTypeTable(OrganizationalModelTable):
default_columns = ('pk', 'name', 'virtual_circuit_count', 'color', 'description')
class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
cid = tables.Column(
linkify=True,
verbose_name=_('Circuit ID')
@@ -63,11 +63,14 @@ class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModel
url_params={'virtual_circuit_id': 'pk'},
verbose_name=_('Terminations')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments')
)
tags = columns.TagColumn(
url_name='circuits:virtualcircuit_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = VirtualCircuit
fields = (
'pk', 'id', 'cid', 'provider', 'provider_account', 'provider_network', 'type', 'status', 'tenant',

View File

@@ -18,6 +18,11 @@ urlpatterns = [
path('circuit-types/<int:pk>/', include(get_model_urls('circuits', 'circuittype'))),
path('circuits/', include(get_model_urls('circuits', 'circuit', detail=False))),
path(
'circuits/<int:pk>/terminations/swap/',
views.CircuitSwapTerminations.as_view(),
name='circuit_terminations_swap'
),
path('circuits/<int:pk>/', include(get_model_urls('circuits', 'circuit'))),
path('circuit-terminations/', include(get_model_urls('circuits', 'circuittermination', detail=False))),

View File

@@ -1,8 +1,13 @@
from django.contrib import messages
from django.db import router, transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext_lazy as _
from dcim.views import PathTraceView
from ipam.models import ASN
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport
from netbox.views import generic
from utilities.forms import ConfirmationForm
from utilities.query import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view
from . import filtersets, forms, tables
@@ -368,6 +373,82 @@ class CircuitBulkDeleteView(generic.BulkDeleteView):
table = tables.CircuitTable
class CircuitSwapTerminations(generic.ObjectEditView):
"""
Swap the A and Z terminations of a circuit.
"""
queryset = Circuit.objects.all()
def get(self, request, pk):
circuit = get_object_or_404(self.queryset, pk=pk)
form = ConfirmationForm()
# Circuit must have at least one termination to swap
if not circuit.termination_a and not circuit.termination_z:
messages.error(request, _(
"No terminations have been defined for circuit {circuit}."
).format(circuit=circuit))
return redirect('circuits:circuit', pk=circuit.pk)
return render(request, 'circuits/circuit_terminations_swap.html', {
'circuit': circuit,
'termination_a': circuit.termination_a,
'termination_z': circuit.termination_z,
'form': form,
'panel_class': 'light',
'button_class': 'primary',
'return_url': circuit.get_absolute_url(),
})
def post(self, request, pk):
circuit = get_object_or_404(self.queryset, pk=pk)
form = ConfirmationForm(request.POST)
if form.is_valid():
termination_a = CircuitTermination.objects.filter(pk=circuit.termination_a_id).first()
termination_z = CircuitTermination.objects.filter(pk=circuit.termination_z_id).first()
if termination_a and termination_z:
# Use a placeholder to avoid an IntegrityError on the (circuit, term_side) unique constraint
with transaction.atomic(using=router.db_for_write(CircuitTermination)):
termination_a.term_side = '_'
termination_a.save()
termination_z.term_side = 'A'
termination_z.save()
termination_a.term_side = 'Z'
termination_a.save()
circuit.refresh_from_db()
circuit.termination_a = termination_z
circuit.termination_z = termination_a
circuit.save()
elif termination_a:
termination_a.term_side = 'Z'
termination_a.save()
circuit.refresh_from_db()
circuit.termination_a = None
circuit.save()
else:
termination_z.term_side = 'A'
termination_z.save()
circuit.refresh_from_db()
circuit.termination_z = None
circuit.save()
messages.success(request, _("Swapped terminations for circuit {circuit}.").format(circuit=circuit))
return redirect('circuits:circuit', pk=circuit.pk)
return render(request, 'circuits/circuit_terminations_swap.html', {
'circuit': circuit,
'termination_a': circuit.termination_a,
'termination_z': circuit.termination_z,
'form': form,
'panel_class': 'default',
'button_class': 'primary',
'return_url': circuit.get_absolute_url(),
})
#
# Circuit terminations
#

View File

@@ -1,7 +1,7 @@
from core.choices import *
from core.models import DataFile, DataSource
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from netbox.utils import get_data_backend_choices
__all__ = (
@@ -10,7 +10,7 @@ __all__ = (
)
class DataSourceSerializer(PrimaryModelSerializer):
class DataSourceSerializer(NetBoxModelSerializer):
type = ChoiceField(
choices=get_data_backend_choices()
)
@@ -26,8 +26,8 @@ class DataSourceSerializer(PrimaryModelSerializer):
model = DataSource
fields = [
'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description',
'sync_interval', 'parameters', 'ignore_rules', 'owner', 'comments', 'custom_fields', 'created',
'last_updated', 'last_synced', 'file_count',
'sync_interval', 'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated',
'last_synced', 'file_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -3,7 +3,7 @@ from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import gettext as _
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, PrimaryModelFilterSet
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.filters import ContentTypeFilter
@@ -20,7 +20,7 @@ __all__ = (
)
class DataSourceFilterSet(PrimaryModelFilterSet):
class DataSourceFilterSet(NetBoxModelFilterSet):
type = django_filters.MultipleChoiceFilter(
choices=get_data_backend_choices,
null_value=None
@@ -80,7 +80,6 @@ class JobFilterSet(BaseFilterSet):
method='search',
label=_('Search'),
)
object_type = ContentTypeFilter()
created = django_filters.DateTimeFilter()
created__before = django_filters.DateTimeFilter(
field_name='created',
@@ -170,7 +169,6 @@ class ObjectChangeFilterSet(BaseFilterSet):
changed_object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ContentType.objects.all()
)
related_object_type = ContentTypeFilter()
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label=_('User (ID)'),

View File

@@ -3,8 +3,9 @@ from django.utils.translation import gettext_lazy as _
from core.choices import JobIntervalChoices
from core.models import *
from netbox.forms import PrimaryModelBulkEditForm
from netbox.forms import NetBoxModelBulkEditForm
from netbox.utils import get_data_backend_choices
from utilities.forms.fields import CommentField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect
@@ -13,7 +14,7 @@ __all__ = (
)
class DataSourceBulkEditForm(PrimaryModelBulkEditForm):
class DataSourceBulkEditForm(NetBoxModelBulkEditForm):
type = forms.ChoiceField(
label=_('Type'),
choices=get_data_backend_choices,
@@ -24,11 +25,17 @@ class DataSourceBulkEditForm(PrimaryModelBulkEditForm):
widget=BulkEditNullBooleanSelect(),
label=_('Enabled')
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
sync_interval = forms.ChoiceField(
choices=JobIntervalChoices,
required=False,
label=_('Sync interval')
)
comments = CommentField()
parameters = forms.JSONField(
label=_('Parameters'),
required=False

View File

@@ -1,16 +1,16 @@
from core.models import *
from netbox.forms import PrimaryModelImportForm
from netbox.forms import NetBoxModelImportForm
__all__ = (
'DataSourceImportForm',
)
class DataSourceImportForm(PrimaryModelImportForm):
class DataSourceImportForm(NetBoxModelImportForm):
class Meta:
model = DataSource
fields = (
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'parameters', 'ignore_rules',
'owner', 'comments',
'comments',
)

View File

@@ -3,13 +3,13 @@ from django.utils.translation import gettext_lazy as _
from core.choices import *
from core.models import *
from netbox.forms import NetBoxModelFilterSetForm, PrimaryModelFilterSetForm
from netbox.forms import NetBoxModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import (
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker
@@ -23,10 +23,10 @@ __all__ = (
)
class DataSourceFilterForm(PrimaryModelFilterSetForm):
class DataSourceFilterForm(NetBoxModelFilterSetForm):
model = DataSource
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id'),
FieldSet('type', 'status', 'enabled', 'sync_interval', name=_('Data Source')),
)
type = forms.MultipleChoiceField(
@@ -51,7 +51,6 @@ class DataSourceFilterForm(PrimaryModelFilterSetForm):
choices=JobIntervalChoices,
required=False
)
tag = TagFilterField(model)
class DataFileFilterForm(NetBoxModelFilterSetForm):

View File

@@ -9,11 +9,11 @@ from django.utils.translation import gettext_lazy as _
from core.forms.mixins import SyncedDataMixin
from core.models import *
from netbox.config import get_config, PARAMS
from netbox.forms import NetBoxModelForm, PrimaryModelForm
from netbox.forms import NetBoxModelForm
from netbox.registry import registry
from netbox.utils import get_data_backend_choices
from utilities.forms import get_field_value
from utilities.forms.fields import JSONField
from utilities.forms.fields import CommentField, JSONField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import HTMXSelect
@@ -26,17 +26,17 @@ __all__ = (
EMPTY_VALUES = ('', None, [], ())
class DataSourceForm(PrimaryModelForm):
class DataSourceForm(NetBoxModelForm):
type = forms.ChoiceField(
choices=get_data_backend_choices,
widget=HTMXSelect()
)
comments = CommentField()
class Meta:
model = DataSource
fields = [
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'owner',
'comments', 'tags',
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'comments', 'tags',
]
widgets = {
'ignore_rules': forms.Textarea(

View File

@@ -5,7 +5,7 @@ import strawberry_django
from django.contrib.contenttypes.models import ContentType as DjangoContentType
from core import models
from netbox.graphql.types import BaseObjectType, PrimaryObjectType
from netbox.graphql.types import BaseObjectType, NetBoxObjectType
from .filters import *
__all__ = (
@@ -32,7 +32,8 @@ class DataFileType(BaseObjectType):
filters=DataSourceFilter,
pagination=True
)
class DataSourceType(PrimaryObjectType):
class DataSourceType(NetBoxObjectType):
datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]

View File

@@ -1,19 +0,0 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0019_configrevision_active'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='datasource',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
]

View File

@@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from core.models import *
from netbox.tables import NetBoxTable, PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from .columns import BackendTypeColumn
from .template_code import DATA_SOURCE_SYNC_BUTTON
@@ -12,7 +12,7 @@ __all__ = (
)
class DataSourceTable(PrimaryModelTable):
class DataSourceTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True,
@@ -42,7 +42,7 @@ class DataSourceTable(PrimaryModelTable):
extra_buttons=DATA_SOURCE_SYNC_BUTTON,
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = DataSource
fields = (
'pk', 'id', 'name', 'type', 'status', 'enabled', 'source_url', 'description', 'sync_interval', 'comments',

View File

@@ -5,9 +5,7 @@ from rest_framework import serializers
from dcim.choices import *
from dcim.models import Cable, CablePath, CableTermination
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import (
BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer, PrimaryModelSerializer,
)
from netbox.api.serializers import BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
@@ -20,7 +18,7 @@ __all__ = (
)
class CableSerializer(PrimaryModelSerializer):
class CableSerializer(NetBoxModelSerializer):
a_terminations = GenericObjectSerializer(many=True, required=False)
b_terminations = GenericObjectSerializer(many=True, required=False)
status = ChoiceField(choices=LinkStatusChoices, required=False)
@@ -31,8 +29,8 @@ class CableSerializer(PrimaryModelSerializer):
model = Cable
fields = [
'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant',
'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'label', 'description')

View File

@@ -11,15 +11,15 @@ from dcim.models import Device, DeviceBay, MACAddress, Module, VirtualDeviceCont
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
from ipam.api.serializers_.ip import IPAddressSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from virtualization.api.serializers_.clusters import ClusterSerializer
from .devicetypes import *
from .nested import NestedDeviceBaySerializer, NestedDeviceSerializer, NestedModuleBaySerializer
from .platforms import PlatformSerializer
from .racks import RackSerializer
from .roles import DeviceRoleSerializer
from .nested import NestedDeviceBaySerializer, NestedDeviceSerializer, NestedModuleBaySerializer
from .sites import LocationSerializer, SiteSerializer
from .virtualchassis import VirtualChassisSerializer
@@ -32,7 +32,7 @@ __all__ = (
)
class DeviceSerializer(PrimaryModelSerializer):
class DeviceSerializer(NetBoxModelSerializer):
device_type = DeviceTypeSerializer(nested=True)
role = DeviceRoleSerializer(nested=True)
tenant = TenantSerializer(
@@ -84,8 +84,8 @@ class DeviceSerializer(PrimaryModelSerializer):
'id', 'url', 'display_url', 'display', 'name', 'device_type', 'role', 'tenant', 'platform', 'serial',
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device',
'status', 'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis',
'vc_position', 'vc_priority', 'description', 'owner', 'comments', 'config_template', 'local_context_data',
'tags', 'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
'vc_position', 'vc_priority', 'description', 'comments', 'config_template', 'local_context_data', 'tags',
'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
'power_port_count', 'power_outlet_count', 'interface_count', 'front_port_count', 'rear_port_count',
'device_bay_count', 'module_bay_count', 'inventory_item_count',
]
@@ -122,7 +122,7 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
return obj.get_config_context()
class VirtualDeviceContextSerializer(PrimaryModelSerializer):
class VirtualDeviceContextSerializer(NetBoxModelSerializer):
device = DeviceSerializer(nested=True)
identifier = serializers.IntegerField(allow_null=True, max_value=32767, min_value=0, required=False, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True, default=None)
@@ -138,13 +138,13 @@ class VirtualDeviceContextSerializer(PrimaryModelSerializer):
model = VirtualDeviceContext
fields = [
'id', 'url', 'display_url', 'display', 'name', 'device', 'identifier', 'tenant', 'primary_ip',
'primary_ip4', 'primary_ip6', 'status', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'primary_ip4', 'primary_ip6', 'status', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'interface_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'identifier', 'device', 'description')
class ModuleSerializer(PrimaryModelSerializer):
class ModuleSerializer(NetBoxModelSerializer):
device = DeviceSerializer(nested=True)
module_bay = NestedModuleBaySerializer()
module_type = ModuleTypeSerializer(nested=True)
@@ -154,12 +154,12 @@ class ModuleSerializer(PrimaryModelSerializer):
model = Module
fields = [
'id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial',
'asset_tag', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'asset_tag', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description')
class MACAddressSerializer(PrimaryModelSerializer):
class MACAddressSerializer(NetBoxModelSerializer):
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.filter(MACADDRESS_ASSIGNMENT_MODELS),
required=False,
@@ -171,7 +171,7 @@ class MACAddressSerializer(PrimaryModelSerializer):
model = MACAddress
fields = [
'id', 'url', 'display_url', 'display', 'mac_address', 'assigned_object_type', 'assigned_object_id',
'assigned_object', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'assigned_object', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'mac_address', 'description')

View File

@@ -155,7 +155,7 @@ class PowerOutletTemplateSerializer(ComponentTemplateSerializer):
model = PowerOutletTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type',
'color', 'power_port', 'feed_leg', 'description', 'created', 'last_updated',
'power_port', 'feed_leg', 'description', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -6,7 +6,7 @@ from rest_framework import serializers
from dcim.choices import *
from dcim.models import DeviceType, ModuleType, ModuleTypeProfile
from netbox.api.fields import AttributesField, ChoiceField, RelatedObjectCountField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from netbox.choices import *
from .manufacturers import ManufacturerSerializer
from .platforms import PlatformSerializer
@@ -18,7 +18,7 @@ __all__ = (
)
class DeviceTypeSerializer(PrimaryModelSerializer):
class DeviceTypeSerializer(NetBoxModelSerializer):
manufacturer = ManufacturerSerializer(nested=True)
default_platform = PlatformSerializer(nested=True, required=False, allow_null=True)
u_height = serializers.DecimalField(
@@ -54,7 +54,7 @@ class DeviceTypeSerializer(PrimaryModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number',
'u_height', 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight',
'weight_unit', 'front_image', 'rear_image', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'weight_unit', 'front_image', 'rear_image', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'device_count', 'console_port_template_count',
'console_server_port_template_count', 'power_port_template_count', 'power_outlet_template_count',
'interface_template_count', 'front_port_template_count', 'rear_port_template_count',
@@ -63,18 +63,18 @@ class DeviceTypeSerializer(PrimaryModelSerializer):
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description', 'device_count')
class ModuleTypeProfileSerializer(PrimaryModelSerializer):
class ModuleTypeProfileSerializer(NetBoxModelSerializer):
class Meta:
model = ModuleTypeProfile
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ModuleTypeSerializer(PrimaryModelSerializer):
class ModuleTypeSerializer(NetBoxModelSerializer):
profile = ModuleTypeProfileSerializer(
nested=True,
required=False,
@@ -105,7 +105,7 @@ class ModuleTypeSerializer(PrimaryModelSerializer):
model = ModuleType
fields = [
'id', 'url', 'display_url', 'display', 'profile', 'manufacturer', 'model', 'part_number', 'airflow',
'weight', 'weight_unit', 'description', 'attributes', 'owner', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
'weight', 'weight_unit', 'description', 'attributes', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'profile', 'manufacturer', 'model', 'description')

View File

@@ -1,13 +1,13 @@
from dcim.models import Manufacturer
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import OrganizationalModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
__all__ = (
'ManufacturerSerializer',
)
class ManufacturerSerializer(OrganizationalModelSerializer):
class ManufacturerSerializer(NetBoxModelSerializer):
# Related object counts
devicetype_count = RelatedObjectCountField('device_types')
@@ -17,7 +17,7 @@ class ManufacturerSerializer(OrganizationalModelSerializer):
class Meta:
model = Manufacturer
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'tags', 'custom_fields',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'devicetype_count', 'inventoryitem_count', 'platform_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'devicetype_count')

View File

@@ -24,7 +24,7 @@ class PlatformSerializer(NestedGroupModelSerializer):
model = Platform
fields = [
'id', 'url', 'display_url', 'display', 'parent', 'name', 'slug', 'manufacturer', 'config_template',
'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
'virtualmachine_count', '_depth',
]
brief_fields = (

View File

@@ -1,7 +1,7 @@
from dcim.choices import *
from dcim.models import PowerFeed, PowerPanel
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .base import ConnectedEndpointsSerializer
from .cables import CabledObjectSerializer
@@ -14,7 +14,7 @@ __all__ = (
)
class PowerPanelSerializer(PrimaryModelSerializer):
class PowerPanelSerializer(NetBoxModelSerializer):
site = SiteSerializer(nested=True)
location = LocationSerializer(
nested=True,
@@ -29,13 +29,13 @@ class PowerPanelSerializer(PrimaryModelSerializer):
class Meta:
model = PowerPanel
fields = [
'id', 'url', 'display_url', 'display', 'site', 'location', 'name', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'powerfeed_count', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'site', 'location', 'name', 'description', 'comments', 'tags',
'custom_fields', 'powerfeed_count', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'powerfeed_count')
class PowerFeedSerializer(PrimaryModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
power_panel = PowerPanelSerializer(nested=True)
rack = RackSerializer(
nested=True,
@@ -71,7 +71,6 @@ class PowerFeedSerializer(PrimaryModelSerializer, CabledObjectSerializer, Connec
'id', 'url', 'display_url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply',
'phase', 'voltage', 'amperage', 'max_utilization', 'mark_connected', 'cable', 'cable_end', 'link_peers',
'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
'description', 'tenant', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'_occupied',
'description', 'tenant', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'cable', '_occupied')

View File

@@ -5,7 +5,7 @@ from dcim.choices import *
from dcim.constants import *
from dcim.models import Rack, RackReservation, RackRole, RackType
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from netbox.choices import *
from netbox.config import ConfigItem
from tenancy.api.serializers_.tenants import TenantSerializer
@@ -22,7 +22,7 @@ __all__ = (
)
class RackRoleSerializer(OrganizationalModelSerializer):
class RackRoleSerializer(NetBoxModelSerializer):
# Related object counts
rack_count = RelatedObjectCountField('racks')
@@ -30,13 +30,13 @@ class RackRoleSerializer(OrganizationalModelSerializer):
class Meta:
model = RackRole
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'rack_count',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'rack_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count')
class RackBaseSerializer(PrimaryModelSerializer):
class RackBaseSerializer(NetBoxModelSerializer):
form_factor = ChoiceField(
choices=RackFormFactorChoices,
allow_blank=True,
@@ -71,8 +71,8 @@ class RackTypeSerializer(RackBaseSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'slug', 'description', 'form_factor',
'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth',
'outer_unit', 'weight', 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
'outer_unit', 'weight', 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description')
@@ -130,13 +130,13 @@ class RackSerializer(RackBaseSerializer):
'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status',
'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'weight',
'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit',
'mounting_depth', 'airflow', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created',
'last_updated', 'device_count', 'powerfeed_count',
'mounting_depth', 'airflow', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'device_count', 'powerfeed_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count')
class RackReservationSerializer(PrimaryModelSerializer):
class RackReservationSerializer(NetBoxModelSerializer):
rack = RackSerializer(
nested=True,
)
@@ -157,7 +157,7 @@ class RackReservationSerializer(PrimaryModelSerializer):
model = RackReservation
fields = [
'id', 'url', 'display_url', 'display', 'rack', 'units', 'status', 'created', 'last_updated', 'user',
'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'tenant', 'description', 'comments', 'tags', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'status', 'user', 'description', 'units')

View File

@@ -3,7 +3,7 @@ from rest_framework import serializers
from dcim.models import DeviceRole, InventoryItemRole
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NestedGroupModelSerializer, OrganizationalModelSerializer
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from .nested import NestedDeviceRoleSerializer
__all__ = (
@@ -23,14 +23,14 @@ class DeviceRoleSerializer(NestedGroupModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'parent',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
'owner', 'comments', '_depth',
'comments', '_depth',
]
brief_fields = (
'id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count', '_depth'
)
class InventoryItemRoleSerializer(OrganizationalModelSerializer):
class InventoryItemRoleSerializer(NetBoxModelSerializer):
# Related object counts
inventoryitem_count = RelatedObjectCountField('inventory_items')
@@ -38,7 +38,7 @@ class InventoryItemRoleSerializer(OrganizationalModelSerializer):
class Meta:
model = InventoryItemRole
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'inventoryitem_count',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'inventoryitem_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'inventoryitem_count')

View File

@@ -6,7 +6,7 @@ from dcim.models import Location, Region, Site, SiteGroup
from ipam.api.serializers_.asns import ASNSerializer
from ipam.models import ASN
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .nested import NestedLocationSerializer, NestedRegionSerializer, NestedSiteGroupSerializer
@@ -27,7 +27,7 @@ class RegionSerializer(NestedGroupModelSerializer):
model = Region
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'site_count', 'prefix_count', 'owner', 'comments', '_depth',
'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
@@ -41,12 +41,12 @@ class SiteGroupSerializer(NestedGroupModelSerializer):
model = SiteGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'site_count', 'prefix_count', 'owner', 'comments', '_depth',
'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
class SiteSerializer(PrimaryModelSerializer):
class SiteSerializer(NetBoxModelSerializer):
status = ChoiceField(choices=SiteStatusChoices, required=False)
region = RegionSerializer(nested=True, required=False, allow_null=True)
group = SiteGroupSerializer(nested=True, required=False, allow_null=True)
@@ -72,7 +72,7 @@ class SiteSerializer(PrimaryModelSerializer):
model = Site
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility',
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner',
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude',
'comments', 'asns', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count',
'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
]
@@ -93,6 +93,6 @@ class LocationSerializer(NestedGroupModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count',
'prefix_count', 'owner', 'comments', '_depth',
'prefix_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')

View File

@@ -1,7 +1,7 @@
from rest_framework import serializers
from dcim.models import VirtualChassis
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from .nested import NestedDeviceSerializer
__all__ = (
@@ -9,7 +9,7 @@ __all__ = (
)
class VirtualChassisSerializer(PrimaryModelSerializer):
class VirtualChassisSerializer(NetBoxModelSerializer):
master = NestedDeviceSerializer(required=False, allow_null=True, default=None)
members = NestedDeviceSerializer(many=True, read_only=True)
@@ -19,7 +19,7 @@ class VirtualChassisSerializer(PrimaryModelSerializer):
class Meta:
model = VirtualChassis
fields = [
'id', 'url', 'display_url', 'display', 'name', 'domain', 'master', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated', 'member_count', 'members',
'id', 'url', 'display_url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'member_count', 'members',
]
brief_fields = ('id', 'url', 'display', 'name', 'master', 'description', 'member_count')

View File

@@ -11,12 +11,11 @@ from ipam.filtersets import PrimaryIPFilterSet
from ipam.models import ASN, IPAddress, VLANTranslationPolicy, VRF
from netbox.choices import ColorChoices
from netbox.filtersets import (
AttributeFiltersMixin, BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet,
OrganizationalModelFilterSet, PrimaryModelFilterSet, NetBoxModelFilterSet,
AttributeFiltersMixin, BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet, NetBoxModelFilterSet,
OrganizationalModelFilterSet,
)
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from tenancy.models import *
from users.filterset_mixins import OwnerFilterMixin
from users.models import User
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
@@ -144,7 +143,7 @@ class SiteGroupFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
status = django_filters.MultipleChoiceFilter(
choices=SiteStatusChoices,
null_value=None
@@ -294,7 +293,7 @@ class RackRoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
class RackTypeFilterSet(PrimaryModelFilterSet):
class RackTypeFilterSet(NetBoxModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label=_('Manufacturer (ID)'),
@@ -329,7 +328,7 @@ class RackTypeFilterSet(PrimaryModelFilterSet):
)
class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -445,7 +444,7 @@ class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterS
)
class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
class RackReservationFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
rack_id = django_filters.ModelMultipleChoiceFilter(
queryset=Rack.objects.all(),
label=_('Rack (ID)'),
@@ -541,7 +540,7 @@ class ManufacturerFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet)
fields = ('id', 'name', 'slug', 'description')
class DeviceTypeFilterSet(PrimaryModelFilterSet):
class DeviceTypeFilterSet(NetBoxModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label=_('Manufacturer (ID)'),
@@ -683,7 +682,7 @@ class DeviceTypeFilterSet(PrimaryModelFilterSet):
return queryset.exclude(inventoryitemtemplates__isnull=value)
class ModuleTypeProfileFilterSet(PrimaryModelFilterSet):
class ModuleTypeProfileFilterSet(NetBoxModelFilterSet):
class Meta:
model = ModuleTypeProfile
@@ -699,7 +698,7 @@ class ModuleTypeProfileFilterSet(PrimaryModelFilterSet):
)
class ModuleTypeFilterSet(AttributeFiltersMixin, PrimaryModelFilterSet):
class ModuleTypeFilterSet(AttributeFiltersMixin, NetBoxModelFilterSet):
profile_id = django_filters.ModelMultipleChoiceFilter(
queryset=ModuleTypeProfile.objects.all(),
label=_('Profile (ID)'),
@@ -843,7 +842,7 @@ class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceType
class Meta:
model = PowerOutletTemplate
fields = ('id', 'name', 'label', 'type', 'color', 'feed_leg', 'description')
fields = ('id', 'name', 'label', 'type', 'feed_leg', 'description')
class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
@@ -952,7 +951,7 @@ class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeCompo
return queryset.filter(qs_filter)
class DeviceRoleFilterSet(NestedGroupModelFilterSet):
class DeviceRoleFilterSet(OrganizationalModelFilterSet):
config_template_id = django_filters.ModelMultipleChoiceFilter(
queryset=ConfigTemplate.objects.all(),
label=_('Config template (ID)'),
@@ -986,7 +985,7 @@ class DeviceRoleFilterSet(NestedGroupModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'vm_role', 'description')
class PlatformFilterSet(NestedGroupModelFilterSet):
class PlatformFilterSet(OrganizationalModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label=_('Immediate parent platform (ID)'),
@@ -1044,7 +1043,7 @@ class PlatformFilterSet(NestedGroupModelFilterSet):
class DeviceFilterSet(
PrimaryModelFilterSet,
NetBoxModelFilterSet,
TenancyFilterSet,
ContactModelFilterSet,
LocalConfigContextFilterSet,
@@ -1346,7 +1345,7 @@ class DeviceFilterSet(
return queryset.exclude(params)
class VirtualDeviceContextFilterSet(PrimaryModelFilterSet, TenancyFilterSet, PrimaryIPFilterSet):
class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet, PrimaryIPFilterSet):
device_id = django_filters.ModelMultipleChoiceFilter(
field_name='device',
queryset=Device.objects.all(),
@@ -1395,7 +1394,7 @@ class VirtualDeviceContextFilterSet(PrimaryModelFilterSet, TenancyFilterSet, Pri
return queryset.exclude(params)
class ModuleFilterSet(PrimaryModelFilterSet):
class ModuleFilterSet(NetBoxModelFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='module_type__manufacturer',
queryset=Manufacturer.objects.all(),
@@ -1517,7 +1516,7 @@ class ModuleFilterSet(PrimaryModelFilterSet):
).distinct()
class DeviceComponentFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
class DeviceComponentFilterSet(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -1683,7 +1682,12 @@ class PathEndpointFilterSet(django_filters.FilterSet):
return queryset.filter(Q(_path__isnull=True) | Q(_path__is_active=False))
class ConsolePortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
class ConsolePortFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet,
PathEndpointFilterSet
):
type = django_filters.MultipleChoiceFilter(
choices=ConsolePortTypeChoices,
null_value=None
@@ -1694,7 +1698,12 @@ class ConsolePortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSe
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
class ConsoleServerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
class ConsoleServerPortFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet,
PathEndpointFilterSet
):
type = django_filters.MultipleChoiceFilter(
choices=ConsolePortTypeChoices,
null_value=None
@@ -1705,7 +1714,12 @@ class ConsoleServerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFi
fields = ('id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end')
class PowerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
class PowerPortFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet,
PathEndpointFilterSet
):
type = django_filters.MultipleChoiceFilter(
choices=PowerPortTypeChoices,
null_value=None
@@ -1718,7 +1732,12 @@ class PowerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet,
)
class PowerOutletFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
class PowerOutletFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet,
PathEndpointFilterSet
):
type = django_filters.MultipleChoiceFilter(
choices=PowerOutletTypeChoices,
null_value=None
@@ -1743,9 +1762,8 @@ class PowerOutletFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSe
)
class MACAddressFilterSet(PrimaryModelFilterSet):
class MACAddressFilterSet(NetBoxModelFilterSet):
mac_address = MultiValueMACAddressFilter()
assigned_object_type = ContentTypeFilter()
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
@@ -1895,6 +1913,7 @@ class CommonInterfaceFilterSet(django_filters.FilterSet):
class InterfaceFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet,
PathEndpointFilterSet,
CommonInterfaceFilterSet
@@ -2055,7 +2074,11 @@ class InterfaceFilterSet(
)
class FrontPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
class FrontPortFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet
):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@@ -2071,7 +2094,11 @@ class FrontPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet)
)
class RearPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
class RearPortFilterSet(
ModularDeviceComponentFilterSet,
NetBoxModelFilterSet,
CabledObjectFilterSet
):
type = django_filters.MultipleChoiceFilter(
choices=PortTypeChoices,
null_value=None
@@ -2084,7 +2111,7 @@ class RearPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
)
class ModuleBayFilterSet(ModularDeviceComponentFilterSet):
class ModuleBayFilterSet(ModularDeviceComponentFilterSet, NetBoxModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=ModuleBay.objects.all(),
label=_('Parent module bay (ID)'),
@@ -2100,7 +2127,7 @@ class ModuleBayFilterSet(ModularDeviceComponentFilterSet):
fields = ('id', 'name', 'label', 'position', 'description')
class DeviceBayFilterSet(DeviceComponentFilterSet):
class DeviceBayFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
installed_device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label=_('Installed device (ID)'),
@@ -2117,7 +2144,7 @@ class DeviceBayFilterSet(DeviceComponentFilterSet):
fields = ('id', 'name', 'label', 'description')
class InventoryItemFilterSet(DeviceComponentFilterSet):
class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=InventoryItem.objects.all(),
label=_('Parent inventory item (ID)'),
@@ -2176,7 +2203,7 @@ class InventoryItemRoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'color', 'description')
class VirtualChassisFilterSet(PrimaryModelFilterSet):
class VirtualChassisFilterSet(NetBoxModelFilterSet):
master_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label=_('Master (ID)'),
@@ -2252,7 +2279,7 @@ class VirtualChassisFilterSet(PrimaryModelFilterSet):
return queryset.filter(qs_filter).distinct()
class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
termination_a_type = ContentTypeFilter(
field_name='terminations__termination_type'
)
@@ -2429,7 +2456,7 @@ class CableTerminationFilterSet(ChangeLoggedModelFilterSet):
fields = ('id', 'cable', 'cable_end', 'termination_type', 'termination_id')
class PowerPanelFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
class PowerPanelFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -2487,7 +2514,7 @@ class PowerPanelFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
return queryset.filter(qs_filter)
class PowerFeedFilterSet(PrimaryModelFilterSet, CabledObjectFilterSet, PathEndpointFilterSet, TenancyFilterSet):
class PowerFeedFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet, PathEndpointFilterSet, TenancyFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region',

View File

@@ -10,14 +10,14 @@ from extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices
from ipam.models import ASN, VLAN, VLANGroup, VRF
from netbox.choices import *
from netbox.forms import (
NestedGroupModelBulkEditForm, NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm,
)
from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from netbox.forms import NetBoxModelBulkEditForm
from netbox.forms.mixins import ChangelogMessageMixin
from tenancy.models import Tenant
from users.models import User
from utilities.forms import BulkEditForm, add_blank_choice, form_from_model
from utilities.forms.fields import ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField
from utilities.forms.fields import (
ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField,
)
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
from virtualization.models import Cluster
@@ -71,12 +71,18 @@ __all__ = (
)
class RegionBulkEditForm(NestedGroupModelBulkEditForm):
class RegionBulkEditForm(NetBoxModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Region.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Region
fieldsets = (
@@ -85,12 +91,18 @@ class RegionBulkEditForm(NestedGroupModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
class SiteGroupBulkEditForm(NestedGroupModelBulkEditForm):
class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=SiteGroup.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = SiteGroup
fieldsets = (
@@ -99,7 +111,7 @@ class SiteGroupBulkEditForm(NestedGroupModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
class SiteBulkEditForm(PrimaryModelBulkEditForm):
class SiteBulkEditForm(NetBoxModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(SiteStatusChoices),
@@ -150,6 +162,12 @@ class SiteBulkEditForm(PrimaryModelBulkEditForm):
choices=add_blank_choice(TimeZoneFormField().choices),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Site
fieldsets = (
@@ -160,7 +178,7 @@ class SiteBulkEditForm(PrimaryModelBulkEditForm):
)
class LocationBulkEditForm(NestedGroupModelBulkEditForm):
class LocationBulkEditForm(NetBoxModelBulkEditForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -190,6 +208,12 @@ class LocationBulkEditForm(NestedGroupModelBulkEditForm):
max_length=50,
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Location
fieldsets = (
@@ -198,11 +222,16 @@ class LocationBulkEditForm(NestedGroupModelBulkEditForm):
nullable_fields = ('parent', 'tenant', 'facility', 'description', 'comments')
class RackRoleBulkEditForm(OrganizationalModelBulkEditForm):
class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
model = RackRole
fieldsets = (
@@ -211,7 +240,7 @@ class RackRoleBulkEditForm(OrganizationalModelBulkEditForm):
nullable_fields = ('color', 'description')
class RackTypeBulkEditForm(PrimaryModelBulkEditForm):
class RackTypeBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -281,6 +310,12 @@ class RackTypeBulkEditForm(PrimaryModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = RackType
fieldsets = (
@@ -299,7 +334,7 @@ class RackTypeBulkEditForm(PrimaryModelBulkEditForm):
)
class RackBulkEditForm(PrimaryModelBulkEditForm):
class RackBulkEditForm(NetBoxModelBulkEditForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -429,6 +464,12 @@ class RackBulkEditForm(PrimaryModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Rack
fieldsets = (
@@ -444,7 +485,7 @@ class RackBulkEditForm(PrimaryModelBulkEditForm):
)
class RackReservationBulkEditForm(PrimaryModelBulkEditForm):
class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(RackReservationStatusChoices),
@@ -461,6 +502,12 @@ class RackReservationBulkEditForm(PrimaryModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = RackReservation
fieldsets = (
@@ -469,7 +516,13 @@ class RackReservationBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('comments',)
class ManufacturerBulkEditForm(OrganizationalModelBulkEditForm):
class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
model = Manufacturer
fieldsets = (
FieldSet('description'),
@@ -477,7 +530,7 @@ class ManufacturerBulkEditForm(OrganizationalModelBulkEditForm):
nullable_fields = ('description',)
class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm):
class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -523,6 +576,12 @@ class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = DeviceType
fieldsets = (
@@ -535,11 +594,17 @@ class DeviceTypeBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments')
class ModuleTypeProfileBulkEditForm(PrimaryModelBulkEditForm):
class ModuleTypeProfileBulkEditForm(NetBoxModelBulkEditForm):
schema = JSONField(
label=_('Schema'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = ModuleTypeProfile
fieldsets = (
@@ -548,7 +613,7 @@ class ModuleTypeProfileBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('description', 'comments')
class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm):
class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
profile = DynamicModelChoiceField(
label=_('Profile'),
queryset=ModuleTypeProfile.objects.all(),
@@ -579,6 +644,12 @@ class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = ModuleType
fieldsets = (
@@ -592,7 +663,7 @@ class ModuleTypeBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('part_number', 'weight', 'weight_unit', 'profile', 'description', 'comments')
class DeviceRoleBulkEditForm(NestedGroupModelBulkEditForm):
class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=DeviceRole.objects.all(),
@@ -612,6 +683,12 @@ class DeviceRoleBulkEditForm(NestedGroupModelBulkEditForm):
queryset=ConfigTemplate.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = DeviceRole
fieldsets = (
@@ -620,7 +697,7 @@ class DeviceRoleBulkEditForm(NestedGroupModelBulkEditForm):
nullable_fields = ('parent', 'color', 'config_template', 'description', 'comments')
class PlatformBulkEditForm(NestedGroupModelBulkEditForm):
class PlatformBulkEditForm(NetBoxModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Platform.objects.all(),
@@ -636,6 +713,12 @@ class PlatformBulkEditForm(NestedGroupModelBulkEditForm):
queryset=ConfigTemplate.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Platform
fieldsets = (
@@ -644,7 +727,7 @@ class PlatformBulkEditForm(NestedGroupModelBulkEditForm):
nullable_fields = ('parent', 'manufacturer', 'config_template', 'description', 'comments')
class DeviceBulkEditForm(PrimaryModelBulkEditForm):
class DeviceBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -704,6 +787,11 @@ class DeviceBulkEditForm(PrimaryModelBulkEditForm):
required=False,
label=_('Serial Number')
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
config_template = DynamicModelChoiceField(
label=_('Config template'),
queryset=ConfigTemplate.objects.all(),
@@ -717,6 +805,7 @@ class DeviceBulkEditForm(PrimaryModelBulkEditForm):
'site_id': ['$site', 'null']
},
)
comments = CommentField()
model = Device
fieldsets = (
@@ -731,7 +820,7 @@ class DeviceBulkEditForm(PrimaryModelBulkEditForm):
)
class ModuleBulkEditForm(PrimaryModelBulkEditForm):
class ModuleBulkEditForm(NetBoxModelBulkEditForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -759,6 +848,12 @@ class ModuleBulkEditForm(PrimaryModelBulkEditForm):
required=False,
label=_('Serial Number')
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Module
fieldsets = (
@@ -767,7 +862,7 @@ class ModuleBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('serial', 'description', 'comments')
class CableBulkEditForm(PrimaryModelBulkEditForm):
class CableBulkEditForm(NetBoxModelBulkEditForm):
type = forms.ChoiceField(
label=_('Type'),
choices=add_blank_choice(CableTypeChoices),
@@ -805,6 +900,12 @@ class CableBulkEditForm(PrimaryModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = Cable
fieldsets = (
@@ -816,12 +917,18 @@ class CableBulkEditForm(PrimaryModelBulkEditForm):
)
class VirtualChassisBulkEditForm(PrimaryModelBulkEditForm):
class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
domain = forms.CharField(
label=_('Domain'),
max_length=30,
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = VirtualChassis
fieldsets = (
@@ -830,7 +937,7 @@ class VirtualChassisBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('domain', 'description', 'comments')
class PowerPanelBulkEditForm(PrimaryModelBulkEditForm):
class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -864,6 +971,12 @@ class PowerPanelBulkEditForm(PrimaryModelBulkEditForm):
'site_id': '$site'
}
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = PowerPanel
fieldsets = (
@@ -872,7 +985,7 @@ class PowerPanelBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('location', 'description', 'comments')
class PowerFeedBulkEditForm(PrimaryModelBulkEditForm):
class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
power_panel = DynamicModelChoiceField(
label=_('Power panel'),
queryset=PowerPanel.objects.all(),
@@ -928,6 +1041,12 @@ class PowerFeedBulkEditForm(PrimaryModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = PowerFeed
fieldsets = (
@@ -1044,10 +1163,6 @@ class PowerOutletTemplateBulkEditForm(ComponentTemplateBulkEditForm):
choices=add_blank_choice(PowerOutletTypeChoices),
required=False
)
color = ColorField(
label=_('Color'),
required=False
)
power_port = forms.ModelChoiceField(
label=_('Power port'),
queryset=PowerPortTemplate.objects.all(),
@@ -1250,7 +1365,7 @@ class InventoryItemTemplateBulkEditForm(ComponentTemplateBulkEditForm):
# Device components
#
class ComponentBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
class ComponentBulkEditForm(NetBoxModelBulkEditForm):
device = forms.ModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1703,11 +1818,16 @@ class InventoryItemBulkEditForm(
# Device component roles
#
class InventoryItemRoleBulkEditForm(OrganizationalModelBulkEditForm):
class InventoryItemRoleBulkEditForm(NetBoxModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
model = InventoryItemRole
fieldsets = (
@@ -1716,7 +1836,7 @@ class InventoryItemRoleBulkEditForm(OrganizationalModelBulkEditForm):
nullable_fields = ('color', 'description')
class VirtualDeviceContextBulkEditForm(PrimaryModelBulkEditForm):
class VirtualDeviceContextBulkEditForm(NetBoxModelBulkEditForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1732,7 +1852,6 @@ class VirtualDeviceContextBulkEditForm(PrimaryModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
model = VirtualDeviceContext
fieldsets = (
FieldSet('device', 'status', 'tenant'),
@@ -1744,7 +1863,14 @@ class VirtualDeviceContextBulkEditForm(PrimaryModelBulkEditForm):
# Addressing
#
class MACAddressBulkEditForm(PrimaryModelBulkEditForm):
class MACAddressBulkEditForm(NetBoxModelBulkEditForm):
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = MACAddress
fieldsets = (
FieldSet('description'),

View File

@@ -11,10 +11,7 @@ from dcim.models import *
from extras.models import ConfigTemplate
from ipam.models import VRF, IPAddress
from netbox.choices import *
from netbox.forms import (
NestedGroupModelImportForm, NetBoxModelImportForm, OrganizationalModelImportForm, OwnerCSVMixin,
PrimaryModelImportForm,
)
from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import (
CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, CSVTypedChoiceField,
@@ -61,7 +58,7 @@ __all__ = (
)
class RegionImportForm(NestedGroupModelImportForm):
class RegionImportForm(NetBoxModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=Region.objects.all(),
@@ -72,10 +69,10 @@ class RegionImportForm(NestedGroupModelImportForm):
class Meta:
model = Region
fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
class SiteGroupImportForm(NestedGroupModelImportForm):
class SiteGroupImportForm(NetBoxModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=SiteGroup.objects.all(),
@@ -86,10 +83,10 @@ class SiteGroupImportForm(NestedGroupModelImportForm):
class Meta:
model = SiteGroup
fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
fields = ('name', 'slug', 'parent', 'description', 'comments', 'tags')
class SiteImportForm(PrimaryModelImportForm):
class SiteImportForm(NetBoxModelImportForm):
status = CSVChoiceField(
label=_('Status'),
choices=SiteStatusChoices,
@@ -121,7 +118,7 @@ class SiteImportForm(PrimaryModelImportForm):
model = Site
fields = (
'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'time_zone', 'description',
'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner', 'comments', 'tags'
'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags'
)
help_texts = {
'time_zone': mark_safe(
@@ -132,7 +129,7 @@ class SiteImportForm(PrimaryModelImportForm):
}
class LocationImportForm(NestedGroupModelImportForm):
class LocationImportForm(NetBoxModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -165,8 +162,8 @@ class LocationImportForm(NestedGroupModelImportForm):
class Meta:
model = Location
fields = (
'site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description', 'owner', 'comments',
'tags',
'site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description',
'tags', 'comments',
)
def __init__(self, data=None, *args, **kwargs):
@@ -178,14 +175,15 @@ class LocationImportForm(NestedGroupModelImportForm):
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(**params)
class RackRoleImportForm(OrganizationalModelImportForm):
class RackRoleImportForm(NetBoxModelImportForm):
slug = SlugField()
class Meta:
model = RackRole
fields = ('name', 'slug', 'color', 'description', 'owner', 'tags')
fields = ('name', 'slug', 'color', 'description', 'tags')
class RackTypeImportForm(PrimaryModelImportForm):
class RackTypeImportForm(NetBoxModelImportForm):
manufacturer = forms.ModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -226,14 +224,14 @@ class RackTypeImportForm(PrimaryModelImportForm):
fields = (
'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units',
'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight',
'weight_unit', 'description', 'owner', 'comments', 'tags',
'weight_unit', 'description', 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
class RackImportForm(PrimaryModelImportForm):
class RackImportForm(NetBoxModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -311,8 +309,7 @@ class RackImportForm(PrimaryModelImportForm):
fields = (
'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor', 'serial',
'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit',
'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner', 'comments',
'tags',
'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
@@ -335,7 +332,7 @@ class RackImportForm(PrimaryModelImportForm):
raise forms.ValidationError(_("U height must be set if not specifying a rack type."))
class RackReservationImportForm(PrimaryModelImportForm):
class RackReservationImportForm(NetBoxModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -376,7 +373,7 @@ class RackReservationImportForm(PrimaryModelImportForm):
class Meta:
model = RackReservation
fields = ('site', 'location', 'rack', 'units', 'status', 'tenant', 'description', 'owner', 'comments', 'tags')
fields = ('site', 'location', 'rack', 'units', 'status', 'tenant', 'description', 'comments', 'tags')
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
@@ -395,14 +392,14 @@ class RackReservationImportForm(PrimaryModelImportForm):
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
class ManufacturerImportForm(OrganizationalModelImportForm):
class ManufacturerImportForm(NetBoxModelImportForm):
class Meta:
model = Manufacturer
fields = ('name', 'slug', 'description', 'owner', 'tags')
fields = ('name', 'slug', 'description', 'tags')
class DeviceTypeImportForm(PrimaryModelImportForm):
class DeviceTypeImportForm(NetBoxModelImportForm):
manufacturer = CSVModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -432,21 +429,20 @@ class DeviceTypeImportForm(PrimaryModelImportForm):
model = DeviceType
fields = [
'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization',
'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'owner', 'comments',
'tags',
'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'comments', 'tags',
]
class ModuleTypeProfileImportForm(PrimaryModelImportForm):
class ModuleTypeProfileImportForm(NetBoxModelImportForm):
class Meta:
model = ModuleTypeProfile
fields = [
'name', 'description', 'schema', 'owner', 'comments', 'tags',
'name', 'description', 'schema', 'comments', 'tags',
]
class ModuleTypeImportForm(PrimaryModelImportForm):
class ModuleTypeImportForm(NetBoxModelImportForm):
profile = forms.ModelChoiceField(
label=_('Profile'),
queryset=ModuleTypeProfile.objects.all(),
@@ -480,11 +476,11 @@ class ModuleTypeImportForm(PrimaryModelImportForm):
model = ModuleType
fields = [
'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'profile',
'owner', 'comments', 'tags'
'comments', 'tags'
]
class DeviceRoleImportForm(NestedGroupModelImportForm):
class DeviceRoleImportForm(NetBoxModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=DeviceRole.objects.all(),
@@ -502,15 +498,17 @@ class DeviceRoleImportForm(NestedGroupModelImportForm):
required=False,
help_text=_('Config template')
)
slug = SlugField()
class Meta:
model = DeviceRole
fields = (
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'owner', 'comments', 'tags'
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags'
)
class PlatformImportForm(NestedGroupModelImportForm):
class PlatformImportForm(NetBoxModelImportForm):
slug = SlugField()
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=Platform.objects.all(),
@@ -539,11 +537,11 @@ class PlatformImportForm(NestedGroupModelImportForm):
class Meta:
model = Platform
fields = (
'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'owner', 'comments', 'tags',
'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'tags',
)
class BaseDeviceImportForm(PrimaryModelImportForm):
class BaseDeviceImportForm(NetBoxModelImportForm):
role = CSVModelChoiceField(
label=_('Device role'),
queryset=DeviceRole.objects.all(),
@@ -669,8 +667,8 @@ class DeviceImportForm(BaseDeviceImportForm):
fields = [
'name', 'role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',
'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent', 'device_bay', 'airflow',
'virtual_chassis', 'vc_position', 'vc_priority', 'cluster', 'description', 'config_template', 'owner',
'comments', 'tags',
'virtual_chassis', 'vc_position', 'vc_priority', 'cluster', 'description', 'config_template', 'comments',
'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -717,7 +715,7 @@ class DeviceImportForm(BaseDeviceImportForm):
self.instance.parent_bay = device_bay
class ModuleImportForm(ModuleCommonForm, PrimaryModelImportForm):
class ModuleImportForm(ModuleCommonForm, NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -755,7 +753,7 @@ class ModuleImportForm(ModuleCommonForm, PrimaryModelImportForm):
class Meta:
model = Module
fields = (
'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'status', 'description', 'owner', 'comments',
'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'status', 'description', 'comments',
'replicate_components', 'adopt_components', 'tags',
)
@@ -779,7 +777,7 @@ class ModuleImportForm(ModuleCommonForm, PrimaryModelImportForm):
# Device components
#
class ConsolePortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class ConsolePortImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -802,10 +800,10 @@ class ConsolePortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = ConsolePort
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags')
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags')
class ConsoleServerPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class ConsoleServerPortImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -828,10 +826,10 @@ class ConsoleServerPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = ConsoleServerPort
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags')
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags')
class PowerPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class PowerPortImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -847,12 +845,11 @@ class PowerPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = PowerPort
fields = (
'device', 'name', 'label', 'type', 'mark_connected', 'maximum_draw', 'allocated_draw', 'description',
'owner', 'tags',
'device', 'name', 'label', 'type', 'mark_connected', 'maximum_draw', 'allocated_draw', 'description', 'tags'
)
class PowerOutletImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class PowerOutletImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -882,7 +879,7 @@ class PowerOutletImportForm(OwnerCSVMixin, NetBoxModelImportForm):
model = PowerOutlet
fields = (
'device', 'name', 'label', 'type', 'color', 'mark_connected', 'power_port', 'feed_leg', 'description',
'owner', 'tags',
'tags',
)
def __init__(self, *args, **kwargs):
@@ -908,7 +905,7 @@ class PowerOutletImportForm(OwnerCSVMixin, NetBoxModelImportForm):
self.fields['power_port'].queryset = PowerPort.objects.none()
class InterfaceImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class InterfaceImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -991,7 +988,7 @@ class InterfaceImportForm(OwnerCSVMixin, NetBoxModelImportForm):
fields = (
'device', 'name', 'label', 'parent', 'bridge', 'lag', 'type', 'speed', 'duplex', 'enabled',
'mark_connected', 'wwn', 'vdcs', 'mtu', 'mgmt_only', 'description', 'poe_mode', 'poe_type', 'mode',
'vrf', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'owner', 'tags'
'vrf', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'tags'
)
def __init__(self, data=None, *args, **kwargs):
@@ -1026,7 +1023,7 @@ class InterfaceImportForm(OwnerCSVMixin, NetBoxModelImportForm):
return self.cleaned_data['vdcs']
class FrontPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class FrontPortImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1048,7 +1045,7 @@ class FrontPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
model = FrontPort
fields = (
'device', 'name', 'label', 'type', 'color', 'mark_connected', 'rear_port', 'rear_port_position',
'description', 'owner', 'tags'
'description', 'tags'
)
def __init__(self, *args, **kwargs):
@@ -1074,7 +1071,7 @@ class FrontPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
self.fields['rear_port'].queryset = RearPort.objects.none()
class RearPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class RearPortImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1088,12 +1085,10 @@ class RearPortImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = RearPort
fields = (
'device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description', 'owner', 'tags',
)
fields = ('device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description', 'tags')
class ModuleBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class ModuleBayImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1102,10 +1097,10 @@ class ModuleBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = ModuleBay
fields = ('device', 'name', 'label', 'position', 'description', 'owner', 'tags')
fields = ('device', 'name', 'label', 'position', 'description', 'tags')
class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class DeviceBayImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1124,7 +1119,7 @@ class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = DeviceBay
fields = ('device', 'name', 'label', 'installed_device', 'description', 'owner', 'tags')
fields = ('device', 'name', 'label', 'installed_device', 'description', 'tags')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -1153,7 +1148,7 @@ class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
self.fields['installed_device'].queryset = Device.objects.none()
class InventoryItemImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class InventoryItemImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1200,7 +1195,7 @@ class InventoryItemImportForm(OwnerCSVMixin, NetBoxModelImportForm):
model = InventoryItem
fields = (
'device', 'name', 'label', 'status', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag',
'discovered', 'description', 'owner', 'tags', 'component_type', 'component_name',
'discovered', 'description', 'tags', 'component_type', 'component_name',
)
def __init__(self, *args, **kwargs):
@@ -1263,7 +1258,7 @@ class InventoryItemImportForm(OwnerCSVMixin, NetBoxModelImportForm):
# Device component roles
#
class InventoryItemRoleImportForm(OrganizationalModelImportForm):
class InventoryItemRoleImportForm(NetBoxModelImportForm):
slug = SlugField()
class Meta:
@@ -1275,7 +1270,7 @@ class InventoryItemRoleImportForm(OrganizationalModelImportForm):
# Addressing
#
class MACAddressImportForm(PrimaryModelImportForm):
class MACAddressImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1306,8 +1301,7 @@ class MACAddressImportForm(PrimaryModelImportForm):
class Meta:
model = MACAddress
fields = [
'mac_address', 'device', 'virtual_machine', 'interface', 'is_primary', 'description', 'owner', 'comments',
'tags',
'mac_address', 'device', 'virtual_machine', 'interface', 'is_primary', 'description', 'comments', 'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -1360,7 +1354,7 @@ class MACAddressImportForm(PrimaryModelImportForm):
# Cables
#
class CableImportForm(PrimaryModelImportForm):
class CableImportForm(NetBoxModelImportForm):
# Termination A
side_a_site = CSVModelChoiceField(
label=_('Side A site'),
@@ -1449,7 +1443,7 @@ class CableImportForm(PrimaryModelImportForm):
fields = [
'side_a_site', 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_device', 'side_b_type',
'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description',
'owner', 'comments', 'tags',
'comments', 'tags',
]
def __init__(self, data=None, *args, **kwargs):
@@ -1543,7 +1537,7 @@ class CableImportForm(PrimaryModelImportForm):
#
class VirtualChassisImportForm(PrimaryModelImportForm):
class VirtualChassisImportForm(NetBoxModelImportForm):
master = CSVModelChoiceField(
label=_('Master'),
queryset=Device.objects.all(),
@@ -1554,14 +1548,14 @@ class VirtualChassisImportForm(PrimaryModelImportForm):
class Meta:
model = VirtualChassis
fields = ('name', 'domain', 'master', 'description', 'owner', 'comments', 'tags')
fields = ('name', 'domain', 'master', 'description', 'comments', 'tags')
#
# Power
#
class PowerPanelImportForm(PrimaryModelImportForm):
class PowerPanelImportForm(NetBoxModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -1577,7 +1571,7 @@ class PowerPanelImportForm(PrimaryModelImportForm):
class Meta:
model = PowerPanel
fields = ('site', 'location', 'name', 'description', 'owner', 'comments', 'tags')
fields = ('site', 'location', 'name', 'description', 'comments', 'tags')
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
@@ -1589,7 +1583,7 @@ class PowerPanelImportForm(PrimaryModelImportForm):
self.fields['location'].queryset = self.fields['location'].queryset.filter(**params)
class PowerFeedImportForm(PrimaryModelImportForm):
class PowerFeedImportForm(NetBoxModelImportForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -1647,7 +1641,7 @@ class PowerFeedImportForm(PrimaryModelImportForm):
model = PowerFeed
fields = (
'site', 'power_panel', 'location', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase',
'voltage', 'amperage', 'max_utilization', 'tenant', 'description', 'owner', 'comments', 'tags',
'voltage', 'amperage', 'max_utilization', 'tenant', 'description', 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
@@ -1671,7 +1665,8 @@ class PowerFeedImportForm(PrimaryModelImportForm):
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
class VirtualDeviceContextImportForm(PrimaryModelImportForm):
class VirtualDeviceContextImportForm(NetBoxModelImportForm):
device = CSVModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1706,7 +1701,7 @@ class VirtualDeviceContextImportForm(PrimaryModelImportForm):
class Meta:
fields = [
'name', 'device', 'status', 'tenant', 'identifier', 'owner', 'comments', 'primary_ip4', 'primary_ip6',
'name', 'device', 'status', 'tenant', 'identifier', 'comments', 'primary_ip4', 'primary_ip6',
]
model = VirtualDeviceContext

View File

@@ -8,14 +8,11 @@ from extras.forms import LocalConfigContextFilterForm
from extras.models import ConfigTemplate
from ipam.models import ASN, VRF, VLANTranslationPolicy
from netbox.choices import *
from netbox.forms import (
NestedGroupModelFilterSetForm, NetBoxModelFilterSetForm, OrganizationalModelFilterSetForm,
PrimaryModelFilterSetForm,
)
from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from users.models import Owner, User
from users.models import User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import NumberWithOptions
from virtualization.models import Cluster, ClusterGroup, VirtualMachine
@@ -140,18 +137,12 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('Device Status'),
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class RegionFilterForm(ContactModelFilterForm, NestedGroupModelFilterSetForm):
class RegionFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Region
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('parent_id', name=_('Region')),
FieldSet('q', 'filter_id', 'tag', 'parent_id'),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
parent_id = DynamicModelMultipleChoiceField(
@@ -162,11 +153,10 @@ class RegionFilterForm(ContactModelFilterForm, NestedGroupModelFilterSetForm):
tag = TagFilterField(model)
class SiteGroupFilterForm(ContactModelFilterForm, NestedGroupModelFilterSetForm):
class SiteGroupFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = SiteGroup
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('parent_id', name=_('Site Group')),
FieldSet('q', 'filter_id', 'tag', 'parent_id'),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
parent_id = DynamicModelMultipleChoiceField(
@@ -177,10 +167,10 @@ class SiteGroupFilterForm(ContactModelFilterForm, NestedGroupModelFilterSetForm)
tag = TagFilterField(model)
class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilterSetForm):
class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Site
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('status', 'region_id', 'group_id', 'asn_id', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -209,10 +199,10 @@ class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, PrimaryModelFilt
tag = TagFilterField(model)
class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NestedGroupModelFilterSetForm):
class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Location
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', 'parent_id', 'status', 'facility', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
@@ -257,15 +247,12 @@ class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NestedGroupM
tag = TagFilterField(model)
class RackRoleFilterForm(OrganizationalModelFilterSetForm):
class RackRoleFilterForm(NetBoxModelFilterSetForm):
model = RackRole
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
)
tag = TagFilterField(model)
class RackBaseFilterForm(PrimaryModelFilterSetForm):
class RackBaseFilterForm(NetBoxModelFilterSetForm):
form_factor = forms.MultipleChoiceField(
label=_('Form factor'),
choices=RackFormFactorChoices,
@@ -316,7 +303,7 @@ class RackBaseFilterForm(PrimaryModelFilterSetForm):
class RackTypeFilterForm(RackBaseFilterForm):
model = RackType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('manufacturer_id', 'form_factor', 'width', 'u_height', name=_('Rack Type')),
FieldSet('starting_unit', 'desc_units', name=_('Numbering')),
FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')),
@@ -333,7 +320,7 @@ class RackTypeFilterForm(RackBaseFilterForm):
class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterForm):
model = Rack
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('status', 'role_id', 'manufacturer_id', 'rack_type_id', 'serial', 'asset_tag', name=_('Rack')),
@@ -426,10 +413,10 @@ class RackElevationFilterForm(RackFilterForm):
)
class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = RackReservation
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('status', 'user_id', name=_('Reservation')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Rack')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -484,19 +471,19 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
tag = TagFilterField(model)
class ManufacturerFilterForm(ContactModelFilterForm, OrganizationalModelFilterSetForm):
class ManufacturerFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Manufacturer
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts'))
)
tag = TagFilterField(model)
class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
model = DeviceType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet(
'manufacturer_id', 'default_platform_id', 'part_number', 'subdevice_role', 'airflow', name=_('Hardware')
),
@@ -621,18 +608,18 @@ class DeviceTypeFilterForm(PrimaryModelFilterSetForm):
)
class ModuleTypeProfileFilterForm(PrimaryModelFilterSetForm):
class ModuleTypeProfileFilterForm(NetBoxModelFilterSetForm):
model = ModuleTypeProfile
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
)
selector_fields = ('filter_id', 'q')
class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
class ModuleTypeFilterForm(NetBoxModelFilterSetForm):
model = ModuleType
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('profile_id', 'manufacturer_id', 'part_number', 'airflow', name=_('Hardware')),
FieldSet(
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
@@ -714,12 +701,8 @@ class ModuleTypeFilterForm(PrimaryModelFilterSetForm):
)
class DeviceRoleFilterForm(NestedGroupModelFilterSetForm):
class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
model = DeviceRole
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('parent_id', 'config_template_id', name=_('Device Role'))
)
config_template_id = DynamicModelMultipleChoiceField(
queryset=ConfigTemplate.objects.all(),
required=False,
@@ -733,12 +716,8 @@ class DeviceRoleFilterForm(NestedGroupModelFilterSetForm):
tag = TagFilterField(model)
class PlatformFilterForm(NestedGroupModelFilterSetForm):
class PlatformFilterForm(NetBoxModelFilterSetForm):
model = Platform
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('manufacturer_id', 'parent_id', 'config_template_id', name=_('Platform'))
)
selector_fields = ('filter_id', 'q', 'manufacturer_id')
parent_id = DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(),
@@ -762,11 +741,11 @@ class DeviceFilterForm(
LocalConfigContextFilterForm,
TenancyFilterForm,
ContactModelFilterForm,
PrimaryModelFilterSetForm
NetBoxModelFilterSetForm
):
model = Device
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet('status', 'role_id', 'airflow', 'serial', 'asset_tag', 'mac_address', name=_('Operation')),
FieldSet('manufacturer_id', 'device_type_id', 'platform_id', name=_('Hardware')),
@@ -956,10 +935,13 @@ class DeviceFilterForm(
tag = TagFilterField(model)
class VirtualDeviceContextFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
class VirtualDeviceContextFilterForm(
TenancyFilterForm,
NetBoxModelFilterSetForm
):
model = VirtualDeviceContext
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('device', 'status', 'has_primary_ip', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -983,10 +965,10 @@ class VirtualDeviceContextFilterForm(TenancyFilterForm, PrimaryModelFilterSetFor
tag = TagFilterField(model)
class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, PrimaryModelFilterSetForm):
class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
model = Module
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'device_id', name=_('Location')),
FieldSet('manufacturer_id', 'module_type_id', 'status', 'serial', 'asset_tag', name=_('Hardware')),
)
@@ -1066,10 +1048,10 @@ class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, PrimaryM
tag = TagFilterField(model)
class VirtualChassisFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
class VirtualChassisFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = VirtualChassis
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
@@ -1095,10 +1077,10 @@ class VirtualChassisFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
tag = TagFilterField(model)
class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = Cable
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('site_id', 'location_id', 'rack_id', 'device_id', name=_('Location')),
FieldSet('type', 'status', 'color', 'length', 'length_unit', 'unterminated', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -1179,10 +1161,10 @@ class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
tag = TagFilterField(model)
class PowerPanelFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
class PowerPanelFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = PowerPanel
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
@@ -1218,10 +1200,10 @@ class PowerPanelFilterForm(ContactModelFilterForm, PrimaryModelFilterSetForm):
tag = TagFilterField(model)
class PowerFeedFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
class PowerFeedFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = PowerFeed
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', 'power_panel_id', 'rack_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization', name=_('Attributes')),
@@ -1331,7 +1313,7 @@ class PathEndpointFilterForm(CabledFilterForm):
class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = ConsolePort
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'speed', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1355,7 +1337,7 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = ConsoleServerPort
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'speed', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1380,7 +1362,7 @@ class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterF
class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = PowerPort
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1399,7 +1381,7 @@ class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = PowerOutlet
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'color', 'status', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1428,7 +1410,7 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
model = Interface
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'kind', 'type', 'speed', 'duplex', 'enabled', 'mgmt_only', name=_('Attributes')),
FieldSet('vrf_id', 'l2vpn_id', 'mac_address', 'wwn', name=_('Addressing')),
FieldSet('poe_mode', 'poe_type', name=_('PoE')),
@@ -1553,7 +1535,7 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1577,7 +1559,7 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
model = RearPort
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'type', 'color', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1601,7 +1583,7 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm):
class ModuleBayFilterForm(DeviceComponentFilterForm):
model = ModuleBay
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', 'position', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1619,7 +1601,7 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
class DeviceBayFilterForm(DeviceComponentFilterForm):
model = DeviceBay
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('name', 'label', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
@@ -1633,7 +1615,7 @@ class DeviceBayFilterForm(DeviceComponentFilterForm):
class InventoryItemFilterForm(DeviceComponentFilterForm):
model = InventoryItem
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet(
'name', 'label', 'status', 'role_id', 'manufacturer_id', 'serial', 'asset_tag', 'discovered',
name=_('Attributes')
@@ -1681,11 +1663,8 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
# Device component roles
#
class InventoryItemRoleFilterForm(OrganizationalModelFilterSetForm):
class InventoryItemRoleFilterForm(NetBoxModelFilterSetForm):
model = InventoryItemRole
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
)
tag = TagFilterField(model)
@@ -1693,10 +1672,10 @@ class InventoryItemRoleFilterForm(OrganizationalModelFilterSetForm):
# Addressing
#
class MACAddressFilterForm(PrimaryModelFilterSetForm):
class MACAddressFilterForm(NetBoxModelFilterSetForm):
model = MACAddress
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('mac_address', 'device_id', 'virtual_machine_id', name=_('MAC address')),
)
selector_fields = ('filter_id', 'q', 'device_id', 'virtual_machine_id')

View File

@@ -10,13 +10,13 @@ from dcim.models import *
from extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices
from ipam.models import ASN, IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
from netbox.forms import NestedGroupModelForm, NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from netbox.forms import NetBoxModelForm
from netbox.forms.mixins import ChangelogMessageMixin
from tenancy.forms import TenancyForm
from users.models import User
from utilities.forms import add_blank_choice, get_field_value
from utilities.forms.fields import (
DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SlugField,
CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SlugField,
)
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
from utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK
@@ -75,12 +75,14 @@ __all__ = (
)
class RegionForm(NestedGroupModelForm):
class RegionForm(NetBoxModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Region.objects.all(),
required=False
)
slug = SlugField()
comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags'),
@@ -89,16 +91,18 @@ class RegionForm(NestedGroupModelForm):
class Meta:
model = Region
fields = (
'parent', 'name', 'slug', 'description', 'owner', 'tags', 'comments',
'parent', 'name', 'slug', 'description', 'tags', 'comments',
)
class SiteGroupForm(NestedGroupModelForm):
class SiteGroupForm(NetBoxModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=SiteGroup.objects.all(),
required=False
)
slug = SlugField()
comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags'),
@@ -107,11 +111,11 @@ class SiteGroupForm(NestedGroupModelForm):
class Meta:
model = SiteGroup
fields = (
'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
'parent', 'name', 'slug', 'description', 'comments', 'tags',
)
class SiteForm(TenancyForm, PrimaryModelForm):
class SiteForm(TenancyForm, NetBoxModelForm):
region = DynamicModelChoiceField(
label=_('Region'),
queryset=Region.objects.all(),
@@ -135,6 +139,7 @@ class SiteForm(TenancyForm, PrimaryModelForm):
choices=add_blank_choice(TimeZoneFormField().choices),
required=False
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -149,7 +154,7 @@ class SiteForm(TenancyForm, PrimaryModelForm):
model = Site
fields = (
'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asns', 'time_zone',
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'owner', 'comments', 'tags',
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
)
widgets = {
'physical_address': forms.Textarea(
@@ -165,7 +170,7 @@ class SiteForm(TenancyForm, PrimaryModelForm):
}
class LocationForm(TenancyForm, NestedGroupModelForm):
class LocationForm(TenancyForm, NetBoxModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -179,6 +184,8 @@ class LocationForm(TenancyForm, NestedGroupModelForm):
'site_id': '$site'
}
)
slug = SlugField()
comments = CommentField()
fieldsets = (
FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')),
@@ -188,12 +195,14 @@ class LocationForm(TenancyForm, NestedGroupModelForm):
class Meta:
model = Location
fields = (
'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'owner',
'comments', 'tags',
'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
'facility', 'tags', 'comments',
)
class RackRoleForm(OrganizationalModelForm):
class RackRoleForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Rack Role')),
)
@@ -201,16 +210,17 @@ class RackRoleForm(OrganizationalModelForm):
class Meta:
model = RackRole
fields = [
'name', 'slug', 'color', 'description', 'owner', 'tags',
'name', 'slug', 'color', 'description', 'tags',
]
class RackTypeForm(PrimaryModelForm):
class RackTypeForm(NetBoxModelForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
quick_add=True
)
comments = CommentField()
slug = SlugField(
label=_('Slug'),
slug_source='model'
@@ -232,11 +242,11 @@ class RackTypeForm(PrimaryModelForm):
fields = [
'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units',
'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight',
'weight_unit', 'description', 'owner', 'comments', 'tags',
'weight_unit', 'description', 'comments', 'tags',
]
class RackForm(TenancyForm, PrimaryModelForm):
class RackForm(TenancyForm, NetBoxModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -261,6 +271,7 @@ class RackForm(TenancyForm, PrimaryModelForm):
required=False,
help_text=_("Select a pre-defined rack type, or set physical characteristics below.")
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -277,7 +288,7 @@ class RackForm(TenancyForm, PrimaryModelForm):
'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight',
'weight_unit', 'description', 'owner', 'comments', 'tags',
'weight_unit', 'description', 'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -307,7 +318,7 @@ class RackForm(TenancyForm, PrimaryModelForm):
)
class RackReservationForm(TenancyForm, PrimaryModelForm):
class RackReservationForm(TenancyForm, NetBoxModelForm):
rack = DynamicModelChoiceField(
label=_('Rack'),
queryset=Rack.objects.all(),
@@ -322,6 +333,7 @@ class RackReservationForm(TenancyForm, PrimaryModelForm):
label=_('User'),
queryset=User.objects.order_by('username')
)
comments = CommentField()
fieldsets = (
FieldSet('rack', 'units', 'status', 'user', 'description', 'tags', name=_('Reservation')),
@@ -331,11 +343,13 @@ class RackReservationForm(TenancyForm, PrimaryModelForm):
class Meta:
model = RackReservation
fields = [
'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags',
'rack', 'units', 'status', 'user', 'tenant_group', 'tenant', 'description', 'comments', 'tags',
]
class ManufacturerForm(OrganizationalModelForm):
class ManufacturerForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Manufacturer')),
)
@@ -343,11 +357,11 @@ class ManufacturerForm(OrganizationalModelForm):
class Meta:
model = Manufacturer
fields = [
'name', 'slug', 'description', 'owner', 'tags',
'name', 'slug', 'description', 'tags',
]
class DeviceTypeForm(PrimaryModelForm):
class DeviceTypeForm(NetBoxModelForm):
manufacturer = DynamicModelChoiceField(
label=_('Manufacturer'),
queryset=Manufacturer.objects.all(),
@@ -366,6 +380,7 @@ class DeviceTypeForm(PrimaryModelForm):
label=_('Slug'),
slug_source='model'
)
comments = CommentField()
fieldsets = (
FieldSet('manufacturer', 'model', 'slug', 'default_platform', 'description', 'tags', name=_('Device Type')),
@@ -381,7 +396,7 @@ class DeviceTypeForm(PrimaryModelForm):
fields = [
'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization',
'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
'description', 'owner', 'comments', 'tags',
'description', 'comments', 'tags',
]
widgets = {
'front_image': ClearableFileInput(attrs={
@@ -393,12 +408,13 @@ class DeviceTypeForm(PrimaryModelForm):
}
class ModuleTypeProfileForm(PrimaryModelForm):
class ModuleTypeProfileForm(NetBoxModelForm):
schema = JSONField(
label=_('Schema'),
required=False,
help_text=_("Enter a valid JSON schema to define supported attributes.")
)
comments = CommentField()
fieldsets = (
FieldSet('name', 'description', 'schema', 'tags', name=_('Profile')),
@@ -407,11 +423,11 @@ class ModuleTypeProfileForm(PrimaryModelForm):
class Meta:
model = ModuleTypeProfile
fields = [
'name', 'description', 'schema', 'owner', 'comments', 'tags',
'name', 'description', 'schema', 'comments', 'tags',
]
class ModuleTypeForm(PrimaryModelForm):
class ModuleTypeForm(NetBoxModelForm):
profile = forms.ModelChoiceField(
queryset=ModuleTypeProfile.objects.all(),
label=_('Profile'),
@@ -422,6 +438,7 @@ class ModuleTypeForm(PrimaryModelForm):
label=_('Manufacturer'),
queryset=Manufacturer.objects.all()
)
comments = CommentField()
@property
def fieldsets(self):
@@ -435,7 +452,7 @@ class ModuleTypeForm(PrimaryModelForm):
model = ModuleType
fields = [
'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit',
'owner', 'comments', 'tags',
'comments', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -490,17 +507,19 @@ class ModuleTypeForm(PrimaryModelForm):
return super()._post_clean()
class DeviceRoleForm(NestedGroupModelForm):
class DeviceRoleForm(NetBoxModelForm):
config_template = DynamicModelChoiceField(
label=_('Config template'),
queryset=ConfigTemplate.objects.all(),
required=False
)
slug = SlugField()
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=DeviceRole.objects.all(),
required=False,
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -512,11 +531,11 @@ class DeviceRoleForm(NestedGroupModelForm):
class Meta:
model = DeviceRole
fields = [
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'owner', 'comments', 'tags',
'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags',
]
class PlatformForm(NestedGroupModelForm):
class PlatformForm(NetBoxModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=Platform.objects.all(),
@@ -537,6 +556,7 @@ class PlatformForm(NestedGroupModelForm):
label=_('Slug'),
max_length=64
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -547,11 +567,11 @@ class PlatformForm(NestedGroupModelForm):
class Meta:
model = Platform
fields = [
'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'owner', 'comments', 'tags',
'name', 'slug', 'parent', 'manufacturer', 'config_template', 'description', 'comments', 'tags',
]
class DeviceForm(TenancyForm, PrimaryModelForm):
class DeviceForm(TenancyForm, NetBoxModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -621,6 +641,7 @@ class DeviceForm(TenancyForm, PrimaryModelForm):
'site_id': ['$site', 'null']
},
)
comments = CommentField()
local_context_data = JSONField(
required=False,
label=''
@@ -656,7 +677,7 @@ class DeviceForm(TenancyForm, PrimaryModelForm):
'name', 'role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'location', 'position', 'face',
'latitude', 'longitude', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster',
'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority', 'description', 'config_template',
'owner', 'comments', 'tags', 'local_context_data',
'comments', 'tags', 'local_context_data',
]
def __init__(self, *args, **kwargs):
@@ -721,7 +742,7 @@ class DeviceForm(TenancyForm, PrimaryModelForm):
self.fields['position'].widget.choices = [(position, f'U{position}')]
class ModuleForm(ModuleCommonForm, PrimaryModelForm):
class ModuleForm(ModuleCommonForm, NetBoxModelForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -744,6 +765,7 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm):
},
selector=True
)
comments = CommentField()
replicate_components = forms.BooleanField(
label=_('Replicate components'),
required=False,
@@ -766,7 +788,7 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm):
model = Module
fields = [
'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag', 'tags', 'replicate_components',
'adopt_components', 'description', 'owner', 'comments',
'adopt_components', 'description', 'comments',
]
def __init__(self, *args, **kwargs):
@@ -787,7 +809,7 @@ def get_termination_type_choices():
])
class CableForm(TenancyForm, PrimaryModelForm):
class CableForm(TenancyForm, NetBoxModelForm):
a_terminations_type = forms.ChoiceField(
choices=get_termination_type_choices,
required=False,
@@ -800,16 +822,17 @@ class CableForm(TenancyForm, PrimaryModelForm):
widget=HTMXSelect(),
label=_('Type')
)
comments = CommentField()
class Meta:
model = Cable
fields = [
'a_terminations_type', 'b_terminations_type', 'type', 'status', 'tenant_group', 'tenant', 'label', 'color',
'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
'length', 'length_unit', 'description', 'comments', 'tags',
]
class PowerPanelForm(PrimaryModelForm):
class PowerPanelForm(NetBoxModelForm):
site = DynamicModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),
@@ -823,6 +846,7 @@ class PowerPanelForm(PrimaryModelForm):
'site_id': '$site'
}
)
comments = CommentField()
fieldsets = (
FieldSet('site', 'location', 'name', 'description', 'tags', name=_('Power Panel')),
@@ -831,11 +855,11 @@ class PowerPanelForm(PrimaryModelForm):
class Meta:
model = PowerPanel
fields = [
'site', 'location', 'name', 'description', 'owner', 'comments', 'tags',
'site', 'location', 'name', 'description', 'comments', 'tags',
]
class PowerFeedForm(TenancyForm, PrimaryModelForm):
class PowerFeedForm(TenancyForm, NetBoxModelForm):
power_panel = DynamicModelChoiceField(
label=_('Power panel'),
queryset=PowerPanel.objects.all(),
@@ -848,6 +872,7 @@ class PowerFeedForm(TenancyForm, PrimaryModelForm):
required=False,
selector=True
)
comments = CommentField()
fieldsets = (
FieldSet(
@@ -862,7 +887,7 @@ class PowerFeedForm(TenancyForm, PrimaryModelForm):
model = PowerFeed
fields = [
'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'tenant_group', 'tenant', 'description', 'owner', 'comments', 'tags'
'max_utilization', 'tenant_group', 'tenant', 'description', 'comments', 'tags'
]
@@ -870,17 +895,18 @@ class PowerFeedForm(TenancyForm, PrimaryModelForm):
# Virtual chassis
#
class VirtualChassisForm(PrimaryModelForm):
class VirtualChassisForm(NetBoxModelForm):
master = forms.ModelChoiceField(
label=_('Master'),
queryset=Device.objects.all(),
required=False,
)
comments = CommentField()
class Meta:
model = VirtualChassis
fields = [
'name', 'domain', 'master', 'description', 'owner', 'comments', 'tags',
'name', 'domain', 'master', 'description', 'comments', 'tags',
]
widgets = {
'master': SelectWithPK(),
@@ -1066,14 +1092,14 @@ class PowerOutletTemplateForm(ModularComponentTemplateForm):
FieldSet('device_type', name=_('Device Type')),
FieldSet('module_type', name=_('Module Type')),
),
'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description',
'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
),
)
class Meta:
model = PowerOutletTemplate
fields = [
'device_type', 'module_type', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description',
'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
]
@@ -1334,7 +1360,7 @@ class InventoryItemTemplateForm(ComponentTemplateForm):
# Device components
#
class DeviceComponentForm(OwnerMixin, NetBoxModelForm):
class DeviceComponentForm(NetBoxModelForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1370,7 +1396,7 @@ class ConsolePortForm(ModularDeviceComponentForm):
class Meta:
model = ConsolePort
fields = [
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags',
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
]
@@ -1384,7 +1410,7 @@ class ConsoleServerPortForm(ModularDeviceComponentForm):
class Meta:
model = ConsoleServerPort
fields = [
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'owner', 'tags',
'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags',
]
@@ -1400,7 +1426,7 @@ class PowerPortForm(ModularDeviceComponentForm):
model = PowerPort
fields = [
'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected',
'description', 'owner', 'tags',
'description', 'tags',
]
@@ -1417,7 +1443,7 @@ class PowerOutletForm(ModularDeviceComponentForm):
fieldsets = (
FieldSet(
'device', 'module', 'name', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'mark_connected',
'description', 'owner', 'tags',
'description', 'tags',
),
)
@@ -1561,7 +1587,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
'lag', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode',
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans',
'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'primary_mac_address',
'owner', 'tags',
'tags',
]
widgets = {
'speed': NumberWithOptions(
@@ -1593,7 +1619,7 @@ class FrontPortForm(ModularDeviceComponentForm):
model = FrontPort
fields = [
'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected',
'description', 'owner', 'tags',
'description', 'tags',
]
@@ -1607,8 +1633,7 @@ class RearPortForm(ModularDeviceComponentForm):
class Meta:
model = RearPort
fields = [
'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'owner',
'tags',
'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags',
]
@@ -1620,7 +1645,7 @@ class ModuleBayForm(ModularDeviceComponentForm):
class Meta:
model = ModuleBay
fields = [
'device', 'module', 'name', 'label', 'position', 'description', 'owner', 'tags',
'device', 'module', 'name', 'label', 'position', 'description', 'tags',
]
@@ -1632,7 +1657,7 @@ class DeviceBayForm(DeviceComponentForm):
class Meta:
model = DeviceBay
fields = [
'device', 'name', 'label', 'description', 'owner', 'tags',
'device', 'name', 'label', 'description', 'tags',
]
@@ -1757,7 +1782,7 @@ class InventoryItemForm(DeviceComponentForm):
model = InventoryItem
fields = [
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
'status', 'description', 'owner', 'tags',
'status', 'description', 'tags',
]
def __init__(self, *args, **kwargs):
@@ -1803,7 +1828,12 @@ class InventoryItemForm(DeviceComponentForm):
self.instance.component = None
class InventoryItemRoleForm(OrganizationalModelForm):
# Device component roles
#
class InventoryItemRoleForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Inventory Item Role')),
)
@@ -1811,11 +1841,11 @@ class InventoryItemRoleForm(OrganizationalModelForm):
class Meta:
model = InventoryItemRole
fields = [
'name', 'slug', 'color', 'description', 'owner', 'tags',
'name', 'slug', 'color', 'description', 'tags',
]
class VirtualDeviceContextForm(TenancyForm, PrimaryModelForm):
class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
device = DynamicModelChoiceField(
label=_('Device'),
queryset=Device.objects.all(),
@@ -1851,7 +1881,7 @@ class VirtualDeviceContextForm(TenancyForm, PrimaryModelForm):
class Meta:
model = VirtualDeviceContext
fields = [
'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'owner',
'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant',
'comments', 'tags'
]
@@ -1860,7 +1890,7 @@ class VirtualDeviceContextForm(TenancyForm, PrimaryModelForm):
# Addressing
#
class MACAddressForm(PrimaryModelForm):
class MACAddressForm(NetBoxModelForm):
mac_address = forms.CharField(
required=True,
label=_('MAC address')
@@ -1899,7 +1929,7 @@ class MACAddressForm(PrimaryModelForm):
class Meta:
model = MACAddress
fields = [
'mac_address', 'interface', 'vminterface', 'description', 'owner', 'tags',
'mac_address', 'interface', 'vminterface', 'description', 'tags',
]
def __init__(self, *args, **kwargs):

View File

@@ -434,8 +434,8 @@ class VirtualChassisCreateForm(NetBoxModelForm):
class Meta:
model = VirtualChassis
fields = [
'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'owner', 'members',
'initial_position', 'tags',
'name', 'domain', 'description', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position',
'tags',
]
def clean(self):

View File

@@ -5,13 +5,16 @@ import strawberry_django
from core.graphql.mixins import ChangelogMixin
from dcim import models
from extras.graphql.mixins import ConfigContextMixin, ContactsMixin, ImageAttachmentsMixin
from extras.graphql.mixins import (
ConfigContextMixin,
ContactsMixin,
CustomFieldsMixin,
ImageAttachmentsMixin,
TagsMixin,
)
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
from netbox.graphql.scalars import BigInt
from netbox.graphql.types import (
BaseObjectType, NestedGroupObjectType, NetBoxObjectType, OrganizationalObjectType, PrimaryObjectType,
)
from users.graphql.mixins import OwnerMixin
from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType
from .filters import *
from .mixins import CabledObjectMixin, PathEndpointMixin
@@ -88,7 +91,12 @@ __all__ = (
@strawberry.type
class ComponentType(OwnerMixin, NetBoxObjectType):
class ComponentType(
ChangelogMixin,
CustomFieldsMixin,
TagsMixin,
BaseObjectType
):
"""
Base type for device/VM components
"""
@@ -151,7 +159,7 @@ class CableTerminationType(NetBoxObjectType):
filters=CableFilter,
pagination=True
)
class CableType(PrimaryObjectType):
class CableType(NetBoxObjectType):
color: str
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -228,7 +236,7 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType):
filters=DeviceFilter,
pagination=True
)
class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
console_port_count: BigInt
console_server_port_count: BigInt
power_port_count: BigInt
@@ -331,7 +339,7 @@ class InventoryItemTemplateType(ComponentTemplateType):
filters=DeviceRoleFilter,
pagination=True
)
class DeviceRoleType(NestedGroupObjectType):
class DeviceRoleType(OrganizationalObjectType):
parent: Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')] | None
children: List[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]]
color: str
@@ -347,7 +355,7 @@ class DeviceRoleType(NestedGroupObjectType):
filters=DeviceTypeFilter,
pagination=True
)
class DeviceTypeType(PrimaryObjectType):
class DeviceTypeType(NetBoxObjectType):
console_port_template_count: BigInt
console_server_port_template_count: BigInt
power_port_template_count: BigInt
@@ -404,7 +412,7 @@ class FrontPortTemplateType(ModularComponentTemplateType):
filters=MACAddressFilter,
pagination=True
)
class MACAddressType(PrimaryObjectType):
class MACAddressType(NetBoxObjectType):
mac_address: str
@strawberry_django.field
@@ -504,7 +512,7 @@ class InventoryItemRoleType(OrganizationalObjectType):
filters=LocationFilter,
pagination=True
)
class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NestedGroupObjectType):
class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, OrganizationalObjectType):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
parent: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
@@ -547,7 +555,7 @@ class ManufacturerType(OrganizationalObjectType, ContactsMixin):
filters=ModuleFilter,
pagination=True
)
class ModuleType(PrimaryObjectType):
class ModuleType(NetBoxObjectType):
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]
module_bay: Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]
module_type: Annotated["ModuleTypeType", strawberry.lazy('dcim.graphql.types')]
@@ -594,7 +602,7 @@ class ModuleBayTemplateType(ModularComponentTemplateType):
filters=ModuleTypeProfileFilter,
pagination=True
)
class ModuleTypeProfileType(PrimaryObjectType):
class ModuleTypeProfileType(NetBoxObjectType):
module_types: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
@@ -604,7 +612,7 @@ class ModuleTypeProfileType(PrimaryObjectType):
filters=ModuleTypeFilter,
pagination=True
)
class ModuleTypeType(PrimaryObjectType):
class ModuleTypeType(NetBoxObjectType):
profile: Annotated["ModuleTypeProfileType", strawberry.lazy('dcim.graphql.types')] | None
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
@@ -624,7 +632,7 @@ class ModuleTypeType(PrimaryObjectType):
filters=PlatformFilter,
pagination=True
)
class PlatformType(NestedGroupObjectType):
class PlatformType(OrganizationalObjectType):
parent: Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')] | None
children: List[Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')]]
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None
@@ -640,7 +648,7 @@ class PlatformType(NestedGroupObjectType):
filters=PowerFeedFilter,
pagination=True
)
class PowerFeedType(CabledObjectMixin, PathEndpointMixin, PrimaryObjectType):
class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin):
power_panel: Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')]
rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -665,7 +673,6 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin
)
class PowerOutletTemplateType(ModularComponentTemplateType):
power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None
color: str
@strawberry_django.type(
@@ -674,7 +681,7 @@ class PowerOutletTemplateType(ModularComponentTemplateType):
filters=PowerPanelFilter,
pagination=True
)
class PowerPanelType(ContactsMixin, PrimaryObjectType):
class PowerPanelType(NetBoxObjectType, ContactsMixin):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
@@ -708,7 +715,7 @@ class PowerPortTemplateType(ModularComponentTemplateType):
filters=RackTypeFilter,
pagination=True
)
class RackTypeType(PrimaryObjectType):
class RackTypeType(NetBoxObjectType):
manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
@@ -718,7 +725,7 @@ class RackTypeType(PrimaryObjectType):
filters=RackFilter,
pagination=True
)
class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -737,7 +744,7 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
filters=RackReservationFilter,
pagination=True
)
class RackReservationType(PrimaryObjectType):
class RackReservationType(NetBoxObjectType):
units: List[int]
rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')]
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -786,7 +793,7 @@ class RearPortTemplateType(ModularComponentTemplateType):
filters=RegionFilter,
pagination=True
)
class RegionType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
children: List[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
@@ -812,7 +819,7 @@ class RegionType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
filters=SiteFilter,
pagination=True
)
class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType):
time_zone: str | None
region: Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None
group: Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None
@@ -847,7 +854,7 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
filters=SiteGroupFilter,
pagination=True
)
class SiteGroupType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType):
sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
children: List[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
@@ -873,7 +880,7 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
filters=VirtualChassisFilter,
pagination=True
)
class VirtualChassisType(PrimaryObjectType):
class VirtualChassisType(NetBoxObjectType):
member_count: BigInt
master: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
@@ -886,7 +893,7 @@ class VirtualChassisType(PrimaryObjectType):
filters=VirtualDeviceContextFilter,
pagination=True
)
class VirtualDeviceContextType(PrimaryObjectType):
class VirtualDeviceContextType(NetBoxObjectType):
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
primary_ip4: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
primary_ip6: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None

View File

@@ -1,17 +0,0 @@
import utilities.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dcim', '0215_rackreservation_status'),
]
operations = [
migrations.AddField(
model_name='poweroutlettemplate',
name='color',
field=utilities.fields.ColorField(blank=True, max_length=6),
),
]

View File

@@ -1,243 +0,0 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0216_poweroutlettemplate_color'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='cable',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='consoleport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='consoleserverport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='device',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='devicebay',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='devicerole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='devicetype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='frontport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='interface',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='inventoryitem',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='inventoryitemrole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='location',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='macaddress',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='manufacturer',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='module',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='modulebay',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='moduletype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='moduletypeprofile',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='platform',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='powerfeed',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='poweroutlet',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='powerpanel',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='powerport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='rack',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='rackreservation',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='rackrole',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='racktype',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='rearport',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='region',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='site',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='sitegroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='virtualchassis',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='virtualdevicecontext',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
]

View File

@@ -7,7 +7,6 @@ from mptt.models import MPTTModel, TreeForeignKey
from dcim.choices import *
from dcim.constants import *
from dcim.models.mixins import InterfaceValidationMixin
from netbox.models import ChangeLoggedModel
from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager
@@ -339,10 +338,6 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
blank=True,
null=True
)
color = ColorField(
verbose_name=_('color'),
blank=True
)
power_port = models.ForeignKey(
to='dcim.PowerPortTemplate',
on_delete=models.SET_NULL,
@@ -393,7 +388,6 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
name=self.resolve_name(kwargs.get('module')),
label=self.resolve_label(kwargs.get('module')),
type=self.type,
color=self.color,
power_port=power_port,
feed_leg=self.feed_leg,
**kwargs
@@ -404,7 +398,6 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
return {
'name': self.name,
'type': self.type,
'color': self.color,
'power_port': self.power_port.name if self.power_port else None,
'feed_leg': self.feed_leg,
'label': self.label,
@@ -412,7 +405,7 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
}
class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel):
class InterfaceTemplate(ModularComponentTemplateModel):
"""
A template for a physical data interface on a new Device.
"""
@@ -476,6 +469,8 @@ class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel)
super().clean()
if self.bridge:
if self.pk and self.bridge_id == self.pk:
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
if self.device_type and self.device_type != self.bridge.device_type:
raise ValidationError({
'bridge': _(
@@ -489,6 +484,11 @@ class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel)
).format(bridge=self.bridge)
})
if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({
'rf_role': "Wireless role may be set only on wireless interfaces."
})
def instantiate(self, **kwargs):
return self.component_model(
name=self.resolve_name(kwargs.get('module')),

View File

@@ -11,10 +11,8 @@ from mptt.models import MPTTModel, TreeForeignKey
from dcim.choices import *
from dcim.constants import *
from dcim.fields import WWNField
from dcim.models.mixins import InterfaceValidationMixin
from netbox.choices import ColorChoices
from netbox.models import OrganizationalModel, NetBoxModel
from netbox.models.mixins import OwnerMixin
from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager
from utilities.ordering import naturalize_interface
@@ -41,7 +39,7 @@ __all__ = (
)
class ComponentModel(OwnerMixin, NetBoxModel):
class ComponentModel(NetBoxModel):
"""
An abstract model inherited by any model which has a parent Device.
"""
@@ -678,14 +676,7 @@ class BaseInterface(models.Model):
return self.primary_mac_address.mac_address
class Interface(
InterfaceValidationMixin,
ModularComponentModel,
BaseInterface,
CabledObjectModel,
PathEndpoint,
TrackingModelMixin,
):
class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEndpoint, TrackingModelMixin):
"""
A network interface within a Device. A physical Interface can connect to exactly one other Interface.
"""
@@ -902,6 +893,10 @@ class Interface(
# Bridge validation
# An interface cannot be bridged to itself
if self.pk and self.bridge_id == self.pk:
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
# A bridged interface belongs to the same device or virtual chassis
if self.bridge and self.bridge.device != self.device:
if self.device.virtual_chassis is None:
@@ -947,9 +942,29 @@ class Interface(
)
})
# PoE validation
# Only physical interfaces may have a PoE mode/type assigned
if self.poe_mode and self.is_virtual:
raise ValidationError({
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
})
if self.poe_type and self.is_virtual:
raise ValidationError({
'poe_type': _("Virtual interfaces cannot have a PoE type.")
})
# An interface with a PoE type set must also specify a mode
if self.poe_type and not self.poe_mode:
raise ValidationError({
'poe_type': _("Must specify PoE mode when designating a PoE type.")
})
# Wireless validation
# RF channel may only be set for wireless interfaces
# RF role & channel may only be set for wireless interfaces
if self.rf_role and not self.is_wireless:
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})
if self.rf_channel and not self.is_wireless:
raise ValidationError({'rf_channel': _("Channel may be set only on wireless interfaces.")})

View File

@@ -4,11 +4,8 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from dcim.constants import VIRTUAL_IFACE_TYPES, WIRELESS_IFACE_TYPES
__all__ = (
'CachedScopeMixin',
'InterfaceValidationMixin',
'RenderConfigMixin',
)
@@ -119,33 +116,3 @@ class CachedScopeMixin(models.Model):
self._site = self.scope.site
self._location = self.scope
cache_related_objects.alters_data = True
class InterfaceValidationMixin:
def clean(self):
super().clean()
# An interface cannot be bridged to itself
if self.pk and self.bridge_id == self.pk:
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
# Only physical interfaces may have a PoE mode/type assigned
if self.poe_mode and self.type in VIRTUAL_IFACE_TYPES:
raise ValidationError({
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
})
if self.poe_type and self.type in VIRTUAL_IFACE_TYPES:
raise ValidationError({
'poe_type': _("Virtual interfaces cannot have a PoE type.")
})
# An interface with a PoE type set must also specify a mode
if self.poe_type and not self.poe_mode:
raise ValidationError({
'poe_type': _("Must specify PoE mode when designating a PoE type.")
})
# RF role may be set only for wireless interfaces
if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})

View File

@@ -1,11 +1,11 @@
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django_tables2.utils import Accessor
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
from dcim.models import Cable
from netbox.tables import PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenancyColumnsMixin
from .template_code import CABLE_LENGTH
@@ -48,7 +48,7 @@ class CableTerminationsColumn(tables.Column):
# Cables
#
class CableTable(TenancyColumnsMixin, PrimaryModelTable):
class CableTable(TenancyColumnsMixin, NetBoxTable):
a_terminations = CableTerminationsColumn(
cable_end='A',
orderable=False,
@@ -117,11 +117,12 @@ class CableTable(TenancyColumnsMixin, PrimaryModelTable):
verbose_name=_('Color Name'),
orderable=False
)
comments = columns.MarkdownColumn()
tags = columns.TagColumn(
url_name='dcim:cable_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Cable
fields = (
'pk', 'id', 'label', 'a_terminations', 'b_terminations', 'device_a', 'device_b', 'rack_a', 'rack_b',

View File

@@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
from django_tables2.utils import Accessor
from dcim import models
from netbox.tables import NestedGroupModelTable, NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from .template_code import *
@@ -58,7 +58,15 @@ MACADDRESS_COPY_BUTTON = """
# Device roles
#
class DeviceRoleTable(NestedGroupModelTable):
class DeviceRoleTable(NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
parent = tables.Column(
verbose_name=_('Parent'),
linkify=True,
)
device_count = columns.LinkedCountColumn(
viewname='dcim:device_list',
url_params={'role_id': 'pk'},
@@ -81,7 +89,7 @@ class DeviceRoleTable(NestedGroupModelTable):
url_name='dcim:devicerole_list'
)
class Meta(NestedGroupModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.DeviceRole
fields = (
'pk', 'id', 'name', 'parent', 'device_count', 'vm_count', 'color', 'vm_role', 'config_template',
@@ -94,7 +102,15 @@ class DeviceRoleTable(NestedGroupModelTable):
# Platforms
#
class PlatformTable(NestedGroupModelTable):
class PlatformTable(NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
parent = tables.Column(
verbose_name=_('Parent'),
linkify=True,
)
manufacturer = tables.Column(
verbose_name=_('Manufacturer'),
linkify=True
@@ -117,7 +133,7 @@ class PlatformTable(NestedGroupModelTable):
url_name='dcim:platform_list'
)
class Meta(NestedGroupModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.Platform
fields = (
'pk', 'id', 'name', 'parent', 'manufacturer', 'device_count', 'vm_count', 'slug', 'config_template',
@@ -132,7 +148,7 @@ class PlatformTable(NestedGroupModelTable):
# Devices
#
class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
name = tables.TemplateColumn(
verbose_name=_('Name'),
template_code=DEVICE_LINK,
@@ -233,6 +249,7 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
accessor='parent_bay',
linkify=True
)
comments = columns.MarkdownColumn()
tags = columns.TagColumn(
url_name='dcim:device_list'
)
@@ -267,7 +284,7 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
verbose_name=_('Inventory items')
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.Device
fields = (
'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'role', 'manufacturer', 'device_type',
@@ -1033,7 +1050,7 @@ class DeviceInventoryItemTable(InventoryItemTable):
)
class InventoryItemRoleTable(OrganizationalModelTable):
class InventoryItemRoleTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -1050,7 +1067,7 @@ class InventoryItemRoleTable(OrganizationalModelTable):
url_name='dcim:inventoryitemrole_list'
)
class Meta(OrganizationalModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.InventoryItemRole
fields = (
'pk', 'id', 'name', 'inventoryitem_count', 'color', 'description', 'slug', 'tags', 'actions',
@@ -1062,7 +1079,7 @@ class InventoryItemRoleTable(OrganizationalModelTable):
# Virtual chassis
#
class VirtualChassisTable(PrimaryModelTable):
class VirtualChassisTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -1076,11 +1093,14 @@ class VirtualChassisTable(PrimaryModelTable):
url_params={'virtual_chassis_id': 'pk'},
verbose_name=_('Members')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:virtualchassis_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.VirtualChassis
fields = (
'pk', 'id', 'name', 'domain', 'master', 'member_count', 'description', 'comments', 'tags', 'created',
@@ -1089,7 +1109,7 @@ class VirtualChassisTable(PrimaryModelTable):
default_columns = ('pk', 'name', 'domain', 'master', 'member_count')
class VirtualDeviceContextTable(TenancyColumnsMixin, PrimaryModelTable):
class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -1120,11 +1140,14 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, PrimaryModelTable):
url_params={'vdc_id': 'pk'},
verbose_name=_('Interfaces')
)
comments = columns.MarkdownColumn()
tags = columns.TagColumn(
url_name='dcim:virtualdevicecontext_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.VirtualDeviceContext
fields = (
'pk', 'id', 'name', 'status', 'identifier', 'tenant', 'tenant_group', 'primary_ip', 'primary_ip4',
@@ -1135,7 +1158,7 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, PrimaryModelTable):
)
class MACAddressTable(PrimaryModelTable):
class MACAddressTable(NetBoxTable):
mac_address = tables.TemplateColumn(
template_code=MACADDRESS_LINK,
verbose_name=_('MAC Address')
@@ -1158,7 +1181,7 @@ class MACAddressTable(PrimaryModelTable):
extra_buttons=MACADDRESS_COPY_BUTTON
)
class Meta(PrimaryModelTable.Meta):
class Meta(DeviceComponentTable.Meta):
model = models.MACAddress
fields = (
'pk', 'id', 'mac_address', 'assigned_object_parent', 'assigned_object', 'description', 'comments', 'tags',

View File

@@ -2,7 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from dcim import models
from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from tenancy.tables import ContactsColumnMixin
from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, WEIGHT
@@ -26,7 +26,7 @@ __all__ = (
# Manufacturers
#
class ManufacturerTable(ContactsColumnMixin, OrganizationalModelTable):
class ManufacturerTable(ContactsColumnMixin, NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -60,7 +60,7 @@ class ManufacturerTable(ContactsColumnMixin, OrganizationalModelTable):
url_name='dcim:manufacturer_list'
)
class Meta(OrganizationalModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.Manufacturer
fields = (
'pk', 'id', 'name', 'racktype_count', 'devicetype_count', 'moduletype_count', 'inventoryitem_count',
@@ -76,7 +76,7 @@ class ManufacturerTable(ContactsColumnMixin, OrganizationalModelTable):
# Device types
#
class DeviceTypeTable(PrimaryModelTable):
class DeviceTypeTable(NetBoxTable):
model = tables.Column(
linkify=True,
verbose_name=_('Device Type')
@@ -93,6 +93,9 @@ class DeviceTypeTable(PrimaryModelTable):
verbose_name=_('Full Depth'),
false_mark=None
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:devicetype_list'
)
@@ -145,7 +148,7 @@ class DeviceTypeTable(PrimaryModelTable):
verbose_name=_('Inventory Items')
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = models.DeviceType
fields = (
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height',
@@ -208,9 +211,6 @@ class PowerPortTemplateTable(ComponentTemplateTable):
class PowerOutletTemplateTable(ComponentTemplateTable):
color = columns.ColorColumn(
verbose_name=_('Color'),
)
actions = columns.ActionsColumn(
actions=('edit', 'delete'),
extra_buttons=MODULAR_COMPONENT_TEMPLATE_BUTTONS
@@ -218,7 +218,7 @@ class PowerOutletTemplateTable(ComponentTemplateTable):
class Meta(ComponentTemplateTable.Meta):
model = models.PowerOutletTemplate
fields = ('pk', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', 'actions')
fields = ('pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'actions')
empty_text = "None"

View File

@@ -1,8 +1,8 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from dcim.models import Module, ModuleType, ModuleTypeProfile
from netbox.tables import PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from .template_code import MODULETYPEPROFILE_ATTRIBUTES, WEIGHT
__all__ = (
@@ -12,7 +12,7 @@ __all__ = (
)
class ModuleTypeProfileTable(PrimaryModelTable):
class ModuleTypeProfileTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -23,11 +23,14 @@ class ModuleTypeProfileTable(PrimaryModelTable):
orderable=False,
verbose_name=_('Attributes')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:moduletypeprofile_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = ModuleTypeProfile
fields = (
'pk', 'id', 'name', 'description', 'comments', 'tags', 'created', 'last_updated',
@@ -37,7 +40,7 @@ class ModuleTypeProfileTable(PrimaryModelTable):
)
class ModuleTypeTable(PrimaryModelTable):
class ModuleTypeTable(NetBoxTable):
profile = tables.Column(
verbose_name=_('Profile'),
linkify=True
@@ -61,11 +64,14 @@ class ModuleTypeTable(PrimaryModelTable):
url_params={'module_type_id': 'pk'},
verbose_name=_('Instances')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:moduletype_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = ModuleType
fields = (
'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'description',
@@ -76,7 +82,7 @@ class ModuleTypeTable(PrimaryModelTable):
)
class ModuleTable(PrimaryModelTable):
class ModuleTable(NetBoxTable):
device = tables.Column(
verbose_name=_('Device'),
linkify=True
@@ -97,11 +103,14 @@ class ModuleTable(PrimaryModelTable):
status = columns.ChoiceFieldColumn(
verbose_name=_('Status'),
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:module_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Module
fields = (
'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'status', 'serial', 'asset_tag',

View File

@@ -1,9 +1,10 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from dcim.models import PowerFeed, PowerPanel
from netbox.tables import PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from netbox.tables import NetBoxTable, columns
from .devices import CableTerminationTable
__all__ = (
@@ -16,7 +17,7 @@ __all__ = (
# Power panels
#
class PowerPanelTable(ContactsColumnMixin, PrimaryModelTable):
class PowerPanelTable(ContactsColumnMixin, NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -34,11 +35,14 @@ class PowerPanelTable(ContactsColumnMixin, PrimaryModelTable):
url_params={'power_panel_id': 'pk'},
verbose_name=_('Power Feeds')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:powerpanel_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = PowerPanel
fields = (
'pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'description', 'comments', 'tags',
@@ -53,7 +57,7 @@ class PowerPanelTable(ContactsColumnMixin, PrimaryModelTable):
# We're not using PathEndpointTable for PowerFeed because power connections
# cannot traverse pass-through ports.
class PowerFeedTable(TenancyColumnsMixin, CableTerminationTable, PrimaryModelTable):
class PowerFeedTable(TenancyColumnsMixin, CableTerminationTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -88,11 +92,14 @@ class PowerFeedTable(TenancyColumnsMixin, CableTerminationTable, PrimaryModelTab
linkify=True,
verbose_name=_('Site'),
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:powerfeed_list'
)
class Meta(CableTerminationTable.Meta, PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = PowerFeed
fields = (
'pk', 'id', 'name', 'power_panel', 'site', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage',

View File

@@ -1,9 +1,9 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django_tables2.utils import Accessor
from dcim.models import Rack, RackReservation, RackRole, RackType
from netbox.tables import OrganizationalModelTable, PrimaryModelTable, columns
from netbox.tables import NetBoxTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from .template_code import OUTER_UNIT, WEIGHT
@@ -15,7 +15,11 @@ __all__ = (
)
class RackRoleTable(OrganizationalModelTable):
#
# Rack roles
#
class RackRoleTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -32,7 +36,7 @@ class RackRoleTable(OrganizationalModelTable):
url_name='dcim:rackrole_list'
)
class Meta(OrganizationalModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = RackRole
fields = (
'pk', 'id', 'name', 'rack_count', 'color', 'description', 'slug', 'tags', 'actions', 'created',
@@ -41,7 +45,11 @@ class RackRoleTable(OrganizationalModelTable):
default_columns = ('pk', 'name', 'rack_count', 'color', 'description')
class RackTypeTable(PrimaryModelTable):
#
# Rack Types
#
class RackTypeTable(NetBoxTable):
model = tables.Column(
verbose_name=_('Model'),
linkify=True
@@ -76,6 +84,9 @@ class RackTypeTable(PrimaryModelTable):
template_code=WEIGHT,
order_by=('_abs_max_weight', 'weight_unit')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
instance_count = columns.LinkedCountColumn(
viewname='dcim:rack_list',
url_params={'rack_type_id': 'pk'},
@@ -85,7 +96,7 @@ class RackTypeTable(PrimaryModelTable):
url_name='dcim:rack_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = RackType
fields = (
'pk', 'id', 'model', 'manufacturer', 'form_factor', 'u_height', 'starting_unit', 'width', 'outer_width',
@@ -97,7 +108,11 @@ class RackTypeTable(PrimaryModelTable):
)
class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
#
# Racks
#
class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -129,6 +144,9 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
template_code="{{ value }}U",
verbose_name=_('Height')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
device_count = columns.LinkedCountColumn(
viewname='dcim:device_list',
url_params={'rack_id': 'pk'},
@@ -168,7 +186,7 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
order_by=('_abs_max_weight', 'weight_unit')
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Rack
fields = (
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role',
@@ -183,7 +201,11 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
)
class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable):
#
# Rack reservations
#
class RackReservationTable(TenancyColumnsMixin, NetBoxTable):
reservation = tables.Column(
verbose_name=_('Reservation'),
accessor='pk',
@@ -210,11 +232,14 @@ class RackReservationTable(TenancyColumnsMixin, PrimaryModelTable):
status = columns.ChoiceFieldColumn(
verbose_name=_('Status'),
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:rackreservation_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = RackReservation
fields = (
'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'status', 'user', 'created', 'tenant',

View File

@@ -1,9 +1,10 @@
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from dcim.models import Location, Region, Site, SiteGroup
from netbox.tables import NestedGroupModelTable, PrimaryModelTable, columns
from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin
from netbox.tables import NetBoxTable, columns
from .template_code import LOCATION_BUTTONS
__all__ = (
@@ -14,7 +15,19 @@ __all__ = (
)
class RegionTable(ContactsColumnMixin, NestedGroupModelTable):
#
# Regions
#
class RegionTable(ContactsColumnMixin, NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
parent = tables.Column(
verbose_name=_('Parent'),
linkify=True,
)
site_count = columns.LinkedCountColumn(
viewname='dcim:site_list',
url_params={'region_id': 'pk'},
@@ -23,8 +36,11 @@ class RegionTable(ContactsColumnMixin, NestedGroupModelTable):
tags = columns.TagColumn(
url_name='dcim:region_list'
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
class Meta(NestedGroupModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Region
fields = (
'pk', 'id', 'name', 'parent', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags',
@@ -33,7 +49,19 @@ class RegionTable(ContactsColumnMixin, NestedGroupModelTable):
default_columns = ('pk', 'name', 'site_count', 'description')
class SiteGroupTable(ContactsColumnMixin, NestedGroupModelTable):
#
# Site groups
#
class SiteGroupTable(ContactsColumnMixin, NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
parent = tables.Column(
verbose_name=_('Parent'),
linkify=True,
)
site_count = columns.LinkedCountColumn(
viewname='dcim:site_list',
url_params={'group_id': 'pk'},
@@ -42,8 +70,11 @@ class SiteGroupTable(ContactsColumnMixin, NestedGroupModelTable):
tags = columns.TagColumn(
url_name='dcim:sitegroup_list'
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
class Meta(NestedGroupModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = SiteGroup
fields = (
'pk', 'id', 'name', 'parent', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags',
@@ -52,7 +83,11 @@ class SiteGroupTable(ContactsColumnMixin, NestedGroupModelTable):
default_columns = ('pk', 'name', 'site_count', 'description')
class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
#
# Sites
#
class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -82,11 +117,14 @@ class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
url_params={'site_id': 'pk'},
verbose_name=_('Devices')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='dcim:site_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Site
fields = (
'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'tenant_group', 'asns',
@@ -96,7 +134,19 @@ class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, PrimaryModelTable):
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description')
class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NestedGroupModelTable):
#
# Locations
#
class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
parent = tables.Column(
verbose_name=_('Parent'),
linkify=True,
)
site = tables.Column(
verbose_name=_('Site'),
linkify=True
@@ -125,8 +175,11 @@ class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NestedGroupModelTa
actions = columns.ActionsColumn(
extra_buttons=LOCATION_BUTTONS
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
class Meta(NestedGroupModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = Location
fields = (
'pk', 'id', 'name', 'parent', 'site', 'status', 'facility', 'tenant', 'tenant_group', 'rack_count',

View File

@@ -13,8 +13,7 @@ from ipam.choices import VLANQinQRoleChoices
from ipam.models import ASN, RIR, VLAN, VRF
from netbox.api.serializers import GenericObjectSerializer
from tenancy.models import Tenant
from users.constants import TOKEN_PREFIX
from users.models import Token, User
from users.models import User
from utilities.testing import APITestCase, APIViewTestCases, create_test_device, disable_logging
from virtualization.models import Cluster, ClusterType
from wireless.choices import WirelessChannelChoices
@@ -1307,6 +1306,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
}
user_permissions = (
'dcim.view_site', 'dcim.view_rack', 'dcim.view_location', 'dcim.view_devicerole', 'dcim.view_devicetype',
'extras.view_configtemplate',
)
@classmethod
@@ -1486,58 +1486,12 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
device.config_template = configtemplate
device.save()
self.add_permissions('dcim.render_config_device', 'dcim.view_device')
url = reverse('dcim-api:device-render-config', kwargs={'pk': device.pk})
self.add_permissions('dcim.add_device')
url = reverse('dcim-api:device-detail', kwargs={'pk': device.pk}) + 'render-config/'
response = self.client.post(url, {}, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(response.data['content'], f'Config for device {device.name}')
def test_render_config_without_permission(self):
configtemplate = ConfigTemplate.objects.create(
name='Config Template 1',
template_code='Config for device {{ device.name }}'
)
device = Device.objects.first()
device.config_template = configtemplate
device.save()
# No permissions added - user has no render_config permission
url = reverse('dcim-api:device-render-config', kwargs={'pk': device.pk})
response = self.client.post(url, {}, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND)
def test_render_config_token_write_enabled(self):
configtemplate = ConfigTemplate.objects.create(
name='Config Template 1',
template_code='Config for device {{ device.name }}'
)
device = Device.objects.first()
device.config_template = configtemplate
device.save()
self.add_permissions('dcim.render_config_device', 'dcim.view_device')
url = reverse('dcim-api:device-render-config', kwargs={'pk': device.pk})
# Request without token auth should fail with PermissionDenied
response = self.client.post(url, {}, format='json')
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
# Create token with write_enabled=False
token = Token.objects.create(version=2, user=self.user, write_enabled=False)
token_header = f'Bearer {TOKEN_PREFIX}{token.key}.{token.token}'
# Request with write-disabled token should fail
response = self.client.post(url, {}, format='json', HTTP_AUTHORIZATION=token_header)
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
# Enable write and retry
token.write_enabled = True
token.save()
response = self.client.post(url, {}, format='json', HTTP_AUTHORIZATION=token_header)
self.assertHttpStatus(response, status.HTTP_200_OK)
class ModuleTest(APIViewTestCases.APIViewTestCase):
model = Module

View File

@@ -1919,21 +1919,18 @@ class PowerOutletTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTest
device_type=device_types[0],
name='Power Outlet 1',
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A,
color=ColorChoices.COLOR_RED,
description='foobar1'
),
PowerOutletTemplate(
device_type=device_types[1],
name='Power Outlet 2',
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B,
color=ColorChoices.COLOR_GREEN,
description='foobar2'
),
PowerOutletTemplate(
device_type=device_types[2],
name='Power Outlet 3',
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C,
color=ColorChoices.COLOR_BLUE,
description='foobar3'
),
))
@@ -1946,10 +1943,6 @@ class PowerOutletTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTest
params = {'feed_leg': [PowerOutletFeedLegChoices.FEED_LEG_A, PowerOutletFeedLegChoices.FEED_LEG_B]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_color(self):
params = {'color': [ColorChoices.COLOR_RED, ColorChoices.COLOR_GREEN]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class InterfaceTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ChangeLoggedFilterSetTests):
queryset = InterfaceTemplate.objects.all()

View File

@@ -4,7 +4,6 @@ from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUEST
from netbox.api.authentication import TokenWritePermission
from netbox.api.renderers import TextRenderer
from .serializers import ConfigTemplateSerializer
@@ -65,24 +64,12 @@ class RenderConfigMixin(ConfigTemplateRenderMixin):
"""
Provides a /render-config/ endpoint for REST API views whose model may have a ConfigTemplate assigned.
"""
def get_permissions(self):
# For render_config action, check only token write ability (not model permissions)
if self.action == 'render_config':
return [TokenWritePermission()]
return super().get_permissions()
@action(detail=True, methods=['post'], url_path='render-config', renderer_classes=[JSONRenderer, TextRenderer])
def render_config(self, request, pk):
"""
Resolve and render the preferred ConfigTemplate for this Device.
"""
# Override restrict() on the default queryset to enforce the render_config & view actions
self.queryset = self.queryset.model.objects.restrict(request.user, 'render_config').restrict(
request.user, 'view'
)
instance = self.get_object()
object_type = instance._meta.model_name
configtemplate = instance.get_config_template()
if not configtemplate:

View File

@@ -8,8 +8,7 @@ from dcim.api.serializers_.sites import LocationSerializer, RegionSerializer, Si
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.models import ConfigContext, ConfigContextProfile, Tag
from netbox.api.fields import SerializedPKRelatedField
from netbox.api.serializers import ChangeLogMessageSerializer, PrimaryModelSerializer, ValidatedModelSerializer
from users.api.serializers_.mixins import OwnerMixin
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer, TenantGroupSerializer
from tenancy.models import Tenant, TenantGroup
from virtualization.api.serializers_.clusters import ClusterSerializer, ClusterGroupSerializer, ClusterTypeSerializer
@@ -21,7 +20,13 @@ __all__ = (
)
class ConfigContextProfileSerializer(PrimaryModelSerializer):
class ConfigContextProfileSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
tags = serializers.SlugRelatedField(
queryset=Tag.objects.all(),
slug_field='slug',
required=False,
many=True
)
data_source = DataSourceSerializer(
nested=True,
required=False
@@ -34,13 +39,13 @@ class ConfigContextProfileSerializer(PrimaryModelSerializer):
class Meta:
model = ConfigContextProfile
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'tags', 'owner', 'comments',
'data_source', 'data_path', 'data_file', 'data_synced', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'tags', 'comments', 'data_source',
'data_path', 'data_file', 'data_synced', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ConfigContextSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class ConfigContextSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
profile = ConfigContextProfileSerializer(
nested=True,
required=False,
@@ -151,7 +156,7 @@ class ConfigContextSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedM
fields = [
'id', 'url', 'display_url', 'display', 'name', 'weight', 'profile', 'description', 'is_active', 'regions',
'site_groups', 'sites', 'locations', 'device_types', 'roles', 'platforms', 'cluster_types',
'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'owner', 'tags', 'data_source', 'data_path',
'data_file', 'data_synced', 'data', 'created', 'last_updated',
'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data_source', 'data_path', 'data_file',
'data_synced', 'data', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -2,19 +2,13 @@ from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
from extras.models import ConfigTemplate
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from netbox.api.serializers.features import TaggableModelSerializer
from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'ConfigTemplateSerializer',
)
class ConfigTemplateSerializer(
OwnerMixin,
ChangeLogMessageSerializer,
TaggableModelSerializer,
ValidatedModelSerializer
):
class ConfigTemplateSerializer(ChangeLogMessageSerializer, TaggableModelSerializer, ValidatedModelSerializer):
data_source = DataSourceSerializer(
nested=True,
required=False
@@ -29,6 +23,6 @@ class ConfigTemplateSerializer(
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'environment_params', 'template_code',
'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file',
'data_synced', 'owner', 'tags', 'created', 'last_updated',
'data_synced', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -8,7 +8,6 @@ from extras.choices import *
from extras.models import CustomField, CustomFieldChoiceSet
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'CustomFieldChoiceSetSerializer',
@@ -16,7 +15,7 @@ __all__ = (
)
class CustomFieldChoiceSetSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class CustomFieldChoiceSetSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
base_choices = ChoiceField(
choices=CustomFieldChoiceSetBaseChoices,
required=False
@@ -33,12 +32,12 @@ class CustomFieldChoiceSetSerializer(OwnerMixin, ChangeLogMessageSerializer, Val
model = CustomFieldChoiceSet
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'base_choices', 'extra_choices',
'order_alphabetically', 'choices_count', 'owner', 'created', 'last_updated',
'order_alphabetically', 'choices_count', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
class CustomFieldSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class CustomFieldSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('custom_fields'),
many=True
@@ -65,8 +64,8 @@ class CustomFieldSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedMod
'id', 'url', 'display_url', 'display', 'object_types', 'type', 'related_object_type', 'data_type',
'name', 'label', 'group_name', 'description', 'required', 'unique', 'search_weight', 'filter_logic',
'ui_visible', 'ui_editable', 'is_cloneable', 'default', 'related_object_filter', 'weight',
'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set', 'owner', 'comments',
'created', 'last_updated',
'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set', 'comments', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -2,14 +2,13 @@ from core.models import ObjectType
from extras.models import CustomLink
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'CustomLinkSerializer',
)
class CustomLinkSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class CustomLinkSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('custom_links'),
many=True
@@ -19,6 +18,6 @@ class CustomLinkSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedMode
model = CustomLink
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'enabled', 'link_text', 'link_url',
'weight', 'group_name', 'button_class', 'new_window', 'owner', 'created', 'last_updated',
'weight', 'group_name', 'button_class', 'new_window', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name')

View File

@@ -7,7 +7,6 @@ from extras.choices import *
from extras.models import EventRule, Webhook
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer
from users.api.serializers_.mixins import OwnerMixin
from utilities.api import get_serializer_for_model
from .scripts import ScriptSerializer
@@ -21,7 +20,7 @@ __all__ = (
# Event Rules
#
class EventRuleSerializer(OwnerMixin, NetBoxModelSerializer):
class EventRuleSerializer(NetBoxModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('event_rules'),
many=True
@@ -37,7 +36,7 @@ class EventRuleSerializer(OwnerMixin, NetBoxModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'enabled', 'event_types', 'conditions',
'action_type', 'action_object_type', 'action_object_id', 'action_object', 'description', 'custom_fields',
'owner', 'tags', 'created', 'last_updated',
'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@@ -57,13 +56,13 @@ class EventRuleSerializer(OwnerMixin, NetBoxModelSerializer):
# Webhooks
#
class WebhookSerializer(OwnerMixin, NetBoxModelSerializer):
class WebhookSerializer(NetBoxModelSerializer):
class Meta:
model = Webhook
fields = [
'id', 'url', 'display_url', 'display', 'name', 'description', 'payload_url', 'http_method',
'http_content_type', 'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path',
'custom_fields', 'owner', 'tags', 'created', 'last_updated',
'custom_fields', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -3,14 +3,13 @@ from core.models import ObjectType
from extras.models import ExportTemplate
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'ExportTemplateSerializer',
)
class ExportTemplateSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class ExportTemplateSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('export_templates'),
many=True
@@ -29,6 +28,6 @@ class ExportTemplateSerializer(OwnerMixin, ChangeLogMessageSerializer, Validated
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'description', 'environment_params',
'template_code', 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source',
'data_path', 'data_file', 'data_synced', 'owner', 'created', 'last_updated',
'data_path', 'data_file', 'data_synced', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -2,14 +2,13 @@ from core.models import ObjectType
from extras.models import SavedFilter
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
from users.api.serializers_.mixins import OwnerMixin
__all__ = (
'SavedFilterSerializer',
)
class SavedFilterSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class SavedFilterSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.all(),
many=True
@@ -19,6 +18,6 @@ class SavedFilterSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedMod
model = SavedFilter
fields = [
'id', 'url', 'display_url', 'display', 'object_types', 'name', 'slug', 'description', 'user', 'weight',
'enabled', 'shared', 'parameters', 'owner', 'created', 'last_updated',
'enabled', 'shared', 'parameters', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')

View File

@@ -6,7 +6,6 @@ from extras.models import Tag, TaggedItem
from netbox.api.exceptions import SerializerNotFound
from netbox.api.fields import ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import BaseModelSerializer, ChangeLogMessageSerializer, ValidatedModelSerializer
from users.api.serializers_.mixins import OwnerMixin
from utilities.api import get_serializer_for_model
__all__ = (
@@ -15,7 +14,7 @@ __all__ = (
)
class TagSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedModelSerializer):
class TagSerializer(ChangeLogMessageSerializer, ValidatedModelSerializer):
object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('tags'),
many=True,

View File

@@ -16,7 +16,7 @@ from rq import Worker
from extras import filtersets
from extras.jobs import ScriptJob
from extras.models import *
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired, TokenWritePermission
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.features import SyncedDataMixin
from netbox.api.metadata import ContentTypeMetadata
from netbox.api.renderers import TextRenderer
@@ -238,22 +238,13 @@ class ConfigTemplateViewSet(SyncedDataMixin, ConfigTemplateRenderMixin, NetBoxMo
serializer_class = serializers.ConfigTemplateSerializer
filterset_class = filtersets.ConfigTemplateFilterSet
def get_permissions(self):
# For render action, check only token write ability (not model permissions)
if self.action == 'render':
return [TokenWritePermission()]
return super().get_permissions()
@action(detail=True, methods=['post'], renderer_classes=[JSONRenderer, TextRenderer])
def render(self, request, pk):
"""
Render a ConfigTemplate using the context data provided (if any). If the client requests "text/plain" data,
return the raw rendered content, rather than serialized JSON.
"""
# Override restrict() on the default queryset to enforce the render & view actions
self.queryset = self.queryset.model.objects.restrict(request.user, 'render').restrict(request.user, 'view')
configtemplate = self.get_object()
context = request.data
return self.render_configtemplate(request, configtemplate, context)

View File

@@ -95,7 +95,11 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non
continue
# Compile event data
if event_rule.action_type == EventRuleActionChoices.SCRIPT:
event_data = {}
else:
event_data = event_rule.action_data or {}
event_data.update(data)
# Webhooks
@@ -131,6 +135,20 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non
elif event_rule.action_type == EventRuleActionChoices.SCRIPT:
# Resolve the script from action parameters
script = event_rule.action_object.python_class()
if event_rule.action_data:
form = script.as_form(event_rule.action_data)
if form.is_valid():
form.cleaned_data.pop('_schedule_at')
form.cleaned_data.pop('_interval')
form.cleaned_data.pop('_commit')
event_data.update(form.cleaned_data)
else:
logger.error(
_("Processing event rule {event_rule} failed - Cannot validate script form: {errors}").format(
event_rule=event_rule, errors=form.errors
)
)
continue
# Enqueue a Job to record the script's execution
from extras.jobs import ScriptJob

View File

@@ -5,9 +5,8 @@ from django.utils.translation import gettext as _
from core.models import DataSource, ObjectType
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet, PrimaryModelFilterSet
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
from tenancy.models import Tenant, TenantGroup
from users.filterset_mixins import OwnerFilterMixin
from users.models import Group, User
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
@@ -62,7 +61,7 @@ class ScriptFilterSet(BaseFilterSet):
)
class WebhookFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
class WebhookFilterSet(NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -91,7 +90,7 @@ class WebhookFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
)
class EventRuleFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
class EventRuleFilterSet(NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -131,7 +130,7 @@ class EventRuleFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
return queryset.filter(event_types__overlap=value)
class CustomFieldFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class CustomFieldFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -180,7 +179,7 @@ class CustomFieldFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
)
class CustomFieldChoiceSetFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class CustomFieldChoiceSetFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -208,7 +207,7 @@ class CustomFieldChoiceSetFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet
return queryset.filter(extra_choices__overlap=value)
class CustomLinkFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class CustomLinkFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -238,7 +237,7 @@ class CustomLinkFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
)
class ExportTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class ExportTemplateFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -276,7 +275,7 @@ class ExportTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
)
class SavedFilterFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class SavedFilterFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -495,7 +494,7 @@ class JournalEntryFilterSet(NetBoxModelFilterSet):
return queryset.filter(comments__icontains=value)
class TagFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class TagFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -590,7 +589,7 @@ class TaggedItemFilterSet(BaseFilterSet):
)
class ConfigContextProfileFilterSet(PrimaryModelFilterSet):
class ConfigContextProfileFilterSet(NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -620,7 +619,7 @@ class ConfigContextProfileFilterSet(PrimaryModelFilterSet):
)
class ConfigContextFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class ConfigContextFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
@@ -789,7 +788,7 @@ class ConfigContextFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
)
class ConfigTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
class ConfigTemplateFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),

View File

@@ -4,8 +4,8 @@ from django.utils.translation import gettext_lazy as _
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
from netbox.forms import NetBoxModelBulkEditForm, PrimaryModelBulkEditForm
from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from netbox.forms import NetBoxModelBulkEditForm
from netbox.forms.mixins import ChangelogMessageMixin
from utilities.forms import BulkEditForm, add_blank_choice
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
@@ -30,7 +30,7 @@ __all__ = (
)
class CustomFieldBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class CustomFieldBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=CustomField.objects.all(),
widget=forms.MultipleHiddenInput
@@ -98,7 +98,7 @@ class CustomFieldBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
nullable_fields = ('group_name', 'description', 'choice_set')
class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=CustomFieldChoiceSet.objects.all(),
widget=forms.MultipleHiddenInput
@@ -118,7 +118,7 @@ class CustomFieldChoiceSetBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEd
nullable_fields = ('base_choices', 'description')
class CustomLinkBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class CustomLinkBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=CustomLink.objects.all(),
widget=forms.MultipleHiddenInput
@@ -144,7 +144,7 @@ class CustomLinkBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
)
class ExportTemplateBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class ExportTemplateBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ExportTemplate.objects.all(),
widget=forms.MultipleHiddenInput
@@ -177,7 +177,7 @@ class ExportTemplateBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm
nullable_fields = ('description', 'mime_type', 'file_name', 'file_extension')
class SavedFilterBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class SavedFilterBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=SavedFilter.objects.all(),
widget=forms.MultipleHiddenInput
@@ -233,7 +233,7 @@ class TableConfigBulkEditForm(BulkEditForm):
nullable_fields = ('description',)
class WebhookBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
class WebhookBulkEditForm(NetBoxModelBulkEditForm):
model = Webhook
pk = forms.ModelMultipleChoiceField(
@@ -271,7 +271,7 @@ class WebhookBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
nullable_fields = ('secret', 'ca_file_path')
class EventRuleBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
class EventRuleBulkEditForm(NetBoxModelBulkEditForm):
model = EventRule
pk = forms.ModelMultipleChoiceField(
@@ -297,7 +297,7 @@ class EventRuleBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
nullable_fields = ('description', 'conditions')
class TagBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class TagBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=Tag.objects.all(),
widget=forms.MultipleHiddenInput
@@ -319,11 +319,17 @@ class TagBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
nullable_fields = ('description',)
class ConfigContextProfileBulkEditForm(PrimaryModelBulkEditForm):
class ConfigContextProfileBulkEditForm(NetBoxModelBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConfigContextProfile.objects.all(),
widget=forms.MultipleHiddenInput
)
description = forms.CharField(
label=_('Description'),
required=False,
max_length=100
)
comments = CommentField()
model = ConfigContextProfile
fieldsets = (
@@ -332,7 +338,7 @@ class ConfigContextProfileBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('description',)
class ConfigContextBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class ConfigContextBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConfigContext.objects.all(),
widget=forms.MultipleHiddenInput
@@ -363,7 +369,7 @@ class ConfigContextBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm)
nullable_fields = ('profile', 'description')
class ConfigTemplateBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm):
class ConfigTemplateBulkEditForm(ChangelogMessageMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConfigTemplate.objects.all(),
widget=forms.MultipleHiddenInput

View File

@@ -9,7 +9,7 @@ from core.models import ObjectType
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
from netbox.forms import NetBoxModelImportForm, OwnerCSVMixin, PrimaryModelImportForm
from netbox.forms import NetBoxModelImportForm
from users.models import Group, User
from utilities.forms import CSVModelForm
from utilities.forms.fields import (
@@ -33,7 +33,7 @@ __all__ = (
)
class CustomFieldImportForm(OwnerCSVMixin, CSVModelForm):
class CustomFieldImportForm(CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_fields'),
@@ -75,11 +75,11 @@ class CustomFieldImportForm(OwnerCSVMixin, CSVModelForm):
fields = (
'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'unique',
'description', 'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'owner', 'comments',
'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'comments',
)
class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm):
class CustomFieldChoiceSetImportForm(CSVModelForm):
base_choices = CSVChoiceField(
choices=CustomFieldChoiceSetBaseChoices,
required=False,
@@ -97,7 +97,7 @@ class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm):
class Meta:
model = CustomFieldChoiceSet
fields = (
'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner',
'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
)
def clean_extra_choices(self):
@@ -114,7 +114,7 @@ class CustomFieldChoiceSetImportForm(OwnerCSVMixin, CSVModelForm):
return data
class CustomLinkImportForm(OwnerCSVMixin, CSVModelForm):
class CustomLinkImportForm(CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links'),
@@ -131,11 +131,11 @@ class CustomLinkImportForm(OwnerCSVMixin, CSVModelForm):
model = CustomLink
fields = (
'name', 'object_types', 'enabled', 'weight', 'group_name', 'button_class', 'new_window', 'link_text',
'link_url', 'owner',
'link_url',
)
class ExportTemplateImportForm(OwnerCSVMixin, CSVModelForm):
class ExportTemplateImportForm(CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('export_templates'),
@@ -146,30 +146,30 @@ class ExportTemplateImportForm(OwnerCSVMixin, CSVModelForm):
model = ExportTemplate
fields = (
'name', 'object_types', 'description', 'environment_params', 'mime_type', 'file_name', 'file_extension',
'as_attachment', 'template_code', 'owner',
'as_attachment', 'template_code',
)
class ConfigContextProfileImportForm(PrimaryModelImportForm):
class ConfigContextProfileImportForm(NetBoxModelImportForm):
class Meta:
model = ConfigContextProfile
fields = [
'name', 'description', 'schema', 'owner', 'comments', 'tags',
'name', 'description', 'schema', 'comments', 'tags',
]
class ConfigTemplateImportForm(OwnerCSVMixin, CSVModelForm):
class ConfigTemplateImportForm(CSVModelForm):
class Meta:
model = ConfigTemplate
fields = (
'name', 'description', 'template_code', 'environment_params', 'mime_type', 'file_name', 'file_extension',
'as_attachment', 'owner', 'tags',
'as_attachment', 'tags',
)
class SavedFilterImportForm(OwnerCSVMixin, CSVModelForm):
class SavedFilterImportForm(CSVModelForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.all(),
@@ -179,21 +179,21 @@ class SavedFilterImportForm(OwnerCSVMixin, CSVModelForm):
class Meta:
model = SavedFilter
fields = (
'name', 'slug', 'object_types', 'description', 'weight', 'enabled', 'shared', 'parameters', 'owner',
'name', 'slug', 'object_types', 'description', 'weight', 'enabled', 'shared', 'parameters',
)
class WebhookImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class WebhookImportForm(NetBoxModelImportForm):
class Meta:
model = Webhook
fields = (
'name', 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template',
'secret', 'ssl_verification', 'ca_file_path', 'description', 'owner', 'tags'
'secret', 'ssl_verification', 'ca_file_path', 'description', 'tags'
)
class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class EventRuleImportForm(NetBoxModelImportForm):
object_types = CSVMultipleContentTypeField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('event_rules'),
@@ -214,7 +214,7 @@ class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
model = EventRule
fields = (
'name', 'description', 'enabled', 'conditions', 'object_types', 'event_types', 'action_type',
'owner', 'comments', 'tags'
'comments', 'tags'
)
def clean(self):
@@ -242,7 +242,7 @@ class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
self.instance.action_object_type = ObjectType.objects.get_for_model(script, for_concrete_model=False)
class TagImportForm(OwnerCSVMixin, CSVModelForm):
class TagImportForm(CSVModelForm):
slug = SlugField()
weight = forms.IntegerField(
label=_('Weight'),
@@ -258,7 +258,7 @@ class TagImportForm(OwnerCSVMixin, CSVModelForm):
class Meta:
model = Tag
fields = (
'name', 'slug', 'color', 'weight', 'description', 'object_types', 'owner',
'name', 'slug', 'color', 'weight', 'description', 'object_types',
)

View File

@@ -6,14 +6,13 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
from netbox.forms import NetBoxModelFilterSetForm, PrimaryModelFilterSetForm
from netbox.forms.base import NetBoxModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from tenancy.models import Tenant, TenantGroup
from users.models import Group, Owner, User
from users.models import Group, User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import (
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
TagFilterField,
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker
@@ -116,11 +115,6 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
label=_('Validation regex'),
required=False
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
@@ -136,11 +130,6 @@ class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
choice = forms.CharField(
required=False
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class CustomLinkFilterForm(SavedFiltersMixin, FilterForm):
@@ -172,11 +161,6 @@ class CustomLinkFilterForm(SavedFiltersMixin, FilterForm):
label=_('Weight'),
required=False
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm):
@@ -223,11 +207,6 @@ class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class ImageAttachmentFilterForm(SavedFiltersMixin, FilterForm):
@@ -276,11 +255,6 @@ class SavedFilterFilterForm(SavedFiltersMixin, FilterForm):
label=_('Weight'),
required=False
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class TableConfigFilterForm(SavedFiltersMixin, FilterForm):
@@ -316,7 +290,7 @@ class TableConfigFilterForm(SavedFiltersMixin, FilterForm):
class WebhookFilterForm(NetBoxModelFilterSetForm):
model = Webhook
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('payload_url', 'http_method', 'http_content_type', name=_('Attributes')),
)
http_content_type = forms.CharField(
@@ -332,18 +306,15 @@ class WebhookFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('HTTP method')
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
tag = TagFilterField(model)
class EventRuleFilterForm(NetBoxModelFilterSetForm):
model = EventRule
tag = TagFilterField(model)
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('object_type_id', 'event_type', 'action_type', 'enabled', name=_('Attributes')),
)
object_type_id = ContentTypeMultipleChoiceField(
@@ -368,12 +339,6 @@ class EventRuleFilterForm(NetBoxModelFilterSetForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
tag = TagFilterField(model)
class TagFilterForm(SavedFiltersMixin, FilterForm):
@@ -388,14 +353,9 @@ class TagFilterForm(SavedFiltersMixin, FilterForm):
required=False,
label=_('Allowed object type')
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class ConfigContextProfileFilterForm(PrimaryModelFilterSetForm):
class ConfigContextProfileFilterForm(SavedFiltersMixin, FilterForm):
model = ConfigContextProfile
fieldsets = (
FieldSet('q', 'filter_id'),
@@ -510,11 +470,6 @@ class ConfigContextFilterForm(SavedFiltersMixin, FilterForm):
required=False,
label=_('Tags')
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class ConfigTemplateFilterForm(SavedFiltersMixin, FilterForm):
@@ -557,11 +512,6 @@ class ConfigTemplateFilterForm(SavedFiltersMixin, FilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
owner_id = DynamicModelChoiceField(
queryset=Owner.objects.all(),
required=False,
label=_('Owner'),
)
class LocalConfigContextFilterForm(forms.Form):

View File

@@ -12,8 +12,8 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site
from extras.choices import *
from extras.models import *
from netbox.events import get_event_type_choices
from netbox.forms import NetBoxModelForm, PrimaryModelForm
from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from netbox.forms import NetBoxModelForm
from netbox.forms.mixins import ChangelogMessageMixin
from tenancy.models import Tenant, TenantGroup
from users.models import Group, User
from utilities.forms import get_field_value
@@ -47,7 +47,7 @@ __all__ = (
)
class CustomFieldForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
class CustomFieldForm(ChangelogMessageMixin, forms.ModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_fields'),
@@ -166,7 +166,7 @@ class CustomFieldForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
del self.fields['choice_set']
class CustomFieldChoiceSetForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
class CustomFieldChoiceSetForm(ChangelogMessageMixin, forms.ModelForm):
# TODO: The extra_choices field definition diverge from the CustomFieldChoiceSet model
extra_choices = forms.CharField(
widget=ChoicesWidget(),
@@ -179,7 +179,7 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, OwnerMixin, forms.ModelFor
class Meta:
model = CustomFieldChoiceSet
fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically', 'owner')
fields = ('name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically')
def __init__(self, *args, initial=None, **kwargs):
super().__init__(*args, initial=initial, **kwargs)
@@ -219,7 +219,7 @@ class CustomFieldChoiceSetForm(ChangelogMessageMixin, OwnerMixin, forms.ModelFor
return data
class CustomLinkForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
class CustomLinkForm(ChangelogMessageMixin, forms.ModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links')
@@ -251,7 +251,7 @@ class CustomLinkForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
}
class ExportTemplateForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, forms.ModelForm):
class ExportTemplateForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('export_templates')
@@ -293,7 +293,7 @@ class ExportTemplateForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, for
return self.cleaned_data
class SavedFilterForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
class SavedFilterForm(ChangelogMessageMixin, forms.ModelForm):
slug = SlugField()
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
@@ -427,7 +427,7 @@ class SubscriptionForm(forms.ModelForm):
fields = ('object_type', 'object_id')
class WebhookForm(OwnerMixin, NetBoxModelForm):
class WebhookForm(NetBoxModelForm):
fieldsets = (
FieldSet('name', 'description', 'tags', name=_('Webhook')),
@@ -447,7 +447,7 @@ class WebhookForm(OwnerMixin, NetBoxModelForm):
}
class EventRuleForm(OwnerMixin, NetBoxModelForm):
class EventRuleForm(NetBoxModelForm):
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
queryset=ObjectType.objects.with_feature('event_rules'),
@@ -480,7 +480,7 @@ class EventRuleForm(OwnerMixin, NetBoxModelForm):
model = EventRule
fields = (
'object_types', 'name', 'description', 'enabled', 'event_types', 'conditions', 'action_type',
'action_object_type', 'action_object_id', 'action_data', 'owner', 'comments', 'tags'
'action_object_type', 'action_object_id', 'action_data', 'comments', 'tags'
)
widgets = {
'conditions': forms.Textarea(attrs={'class': 'font-monospace'}),
@@ -563,7 +563,7 @@ class EventRuleForm(OwnerMixin, NetBoxModelForm):
return self.cleaned_data
class TagForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
class TagForm(ChangelogMessageMixin, forms.ModelForm):
slug = SlugField()
object_types = ContentTypeMultipleChoiceField(
label=_('Object types'),
@@ -582,11 +582,11 @@ class TagForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
class Meta:
model = Tag
fields = [
'name', 'slug', 'color', 'weight', 'description', 'object_types', 'owner',
'name', 'slug', 'color', 'weight', 'description', 'object_types',
]
class ConfigContextProfileForm(SyncedDataMixin, PrimaryModelForm):
class ConfigContextProfileForm(SyncedDataMixin, NetBoxModelForm):
schema = JSONField(
label=_('Schema'),
required=False,
@@ -606,12 +606,11 @@ class ConfigContextProfileForm(SyncedDataMixin, PrimaryModelForm):
class Meta:
model = ConfigContextProfile
fields = (
'name', 'description', 'schema', 'data_source', 'data_file', 'auto_sync_enabled', 'owner', 'comments',
'tags',
'name', 'description', 'schema', 'data_source', 'data_file', 'auto_sync_enabled', 'comments', 'tags',
)
class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, forms.ModelForm):
class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm):
profile = DynamicModelChoiceField(
label=_('Profile'),
queryset=ConfigContextProfile.objects.all(),
@@ -702,7 +701,7 @@ class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, form
fields = (
'name', 'weight', 'profile', 'description', 'data', 'is_active', 'regions', 'site_groups', 'sites',
'locations', 'roles', 'device_types', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
'tenant_groups', 'tenants', 'owner', 'tags', 'data_source', 'data_file', 'auto_sync_enabled',
'tenant_groups', 'tenants', 'tags', 'data_source', 'data_file', 'auto_sync_enabled',
)
def __init__(self, *args, initial=None, **kwargs):
@@ -728,7 +727,7 @@ class ConfigContextForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, form
return self.cleaned_data
class ConfigTemplateForm(ChangelogMessageMixin, SyncedDataMixin, OwnerMixin, forms.ModelForm):
class ConfigTemplateForm(ChangelogMessageMixin, SyncedDataMixin, forms.ModelForm):
tags = DynamicModelMultipleChoiceField(
label=_('Tags'),
queryset=Tag.objects.all(),

View File

@@ -6,8 +6,7 @@ import strawberry_django
from core.graphql.mixins import SyncedDataMixin
from extras import models
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin
from netbox.graphql.types import BaseObjectType, ContentTypeType, ObjectType, PrimaryObjectType
from users.graphql.mixins import OwnerMixin
from netbox.graphql.types import BaseObjectType, ContentTypeType, NetBoxObjectType, ObjectType, OrganizationalObjectType
from .filters import *
if TYPE_CHECKING:
@@ -52,7 +51,7 @@ __all__ = (
filters=ConfigContextProfileFilter,
pagination=True
)
class ConfigContextProfileType(SyncedDataMixin, PrimaryObjectType):
class ConfigContextProfileType(SyncedDataMixin, NetBoxObjectType):
pass
@@ -62,7 +61,7 @@ class ConfigContextProfileType(SyncedDataMixin, PrimaryObjectType):
filters=ConfigContextFilter,
pagination=True
)
class ConfigContextType(SyncedDataMixin, OwnerMixin, ObjectType):
class ConfigContextType(SyncedDataMixin, ObjectType):
profile: ConfigContextProfileType | None
roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]
device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]
@@ -85,7 +84,7 @@ class ConfigContextType(SyncedDataMixin, OwnerMixin, ObjectType):
filters=ConfigTemplateFilter,
pagination=True
)
class ConfigTemplateType(SyncedDataMixin, OwnerMixin, TagsMixin, ObjectType):
class ConfigTemplateType(SyncedDataMixin, TagsMixin, ObjectType):
virtualmachines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
platforms: List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
@@ -98,7 +97,7 @@ class ConfigTemplateType(SyncedDataMixin, OwnerMixin, TagsMixin, ObjectType):
filters=CustomFieldFilter,
pagination=True
)
class CustomFieldType(OwnerMixin, ObjectType):
class CustomFieldType(ObjectType):
related_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
choice_set: Annotated["CustomFieldChoiceSetType", strawberry.lazy('extras.graphql.types')] | None
@@ -109,7 +108,7 @@ class CustomFieldType(OwnerMixin, ObjectType):
filters=CustomFieldChoiceSetFilter,
pagination=True
)
class CustomFieldChoiceSetType(OwnerMixin, ObjectType):
class CustomFieldChoiceSetType(ObjectType):
choices_for: List[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]]
extra_choices: List[List[str]] | None
@@ -121,7 +120,7 @@ class CustomFieldChoiceSetType(OwnerMixin, ObjectType):
filters=CustomLinkFilter,
pagination=True
)
class CustomLinkType(OwnerMixin, ObjectType):
class CustomLinkType(ObjectType):
pass
@@ -131,7 +130,7 @@ class CustomLinkType(OwnerMixin, ObjectType):
filters=ExportTemplateFilter,
pagination=True
)
class ExportTemplateType(SyncedDataMixin, OwnerMixin, ObjectType):
class ExportTemplateType(SyncedDataMixin, ObjectType):
pass
@@ -181,7 +180,7 @@ class NotificationGroupType(ObjectType):
filters=SavedFilterFilter,
pagination=True
)
class SavedFilterType(OwnerMixin, ObjectType):
class SavedFilterType(ObjectType):
user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None
@@ -210,7 +209,7 @@ class TableConfigType(ObjectType):
filters=TagFilter,
pagination=True
)
class TagType(OwnerMixin, ObjectType):
class TagType(ObjectType):
color: str
object_types: List[ContentTypeType]
@@ -222,7 +221,7 @@ class TagType(OwnerMixin, ObjectType):
filters=WebhookFilter,
pagination=True
)
class WebhookType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType):
class WebhookType(OrganizationalObjectType):
pass
@@ -232,5 +231,5 @@ class WebhookType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType):
filters=EventRuleFilter,
pagination=True
)
class EventRuleType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType):
class EventRuleType(OrganizationalObjectType):
action_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None

View File

@@ -1,89 +0,0 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('extras', '0133_make_cf_minmax_decimal'),
('users', '0015_owner'),
]
operations = [
migrations.AddField(
model_name='configcontext',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='configcontextprofile',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='configtemplate',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='customfield',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='customfieldchoiceset',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='customlink',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='eventrule',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='exporttemplate',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='savedfilter',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='tag',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='webhook',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
]

View File

@@ -13,7 +13,6 @@ from extras.models.mixins import RenderTemplateMixin
from extras.querysets import ConfigContextQuerySet
from netbox.models import ChangeLoggedModel, PrimaryModel
from netbox.models.features import CloningMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin
from netbox.models.mixins import OwnerMixin
from utilities.data import deepmerge
from utilities.jsonschema import validate_schema
@@ -69,7 +68,7 @@ class ConfigContextProfile(SyncedDataMixin, PrimaryModel):
sync_data.alters_data = True
class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, OwnerMixin, ChangeLoggedModel):
class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, ChangeLoggedModel):
"""
A ConfigContext represents a set of arbitrary data available to any Device or VirtualMachine matching its assigned
qualifiers (region, site, etc.). For example, the data stored in a ConfigContext assigned to site A and tenant B
@@ -267,13 +266,7 @@ class ConfigContextModel(models.Model):
#
class ConfigTemplate(
RenderTemplateMixin,
SyncedDataMixin,
CustomLinksMixin,
ExportTemplatesMixin,
OwnerMixin,
TagsMixin,
ChangeLoggedModel,
RenderTemplateMixin, SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel
):
name = models.CharField(
verbose_name=_('name'),

View File

@@ -21,7 +21,6 @@ from extras.choices import *
from extras.data import CHOICE_SETS
from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin
from netbox.models.mixins import OwnerMixin
from netbox.search import FieldTypes
from utilities import filters
from utilities.datetime import datetime_from_timestamp
@@ -71,7 +70,7 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
}
class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
object_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='custom_fields',
@@ -774,7 +773,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo
raise ValidationError(_("Required field cannot be empty."))
class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
"""
Represents a set of choices available for choice and multi-choice custom fields.
"""

View File

@@ -25,7 +25,6 @@ from netbox.models import ChangeLoggedModel
from netbox.models.features import (
CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin, has_feature
)
from netbox.models.mixins import OwnerMixin
from utilities.html import clean_html
from utilities.jinja2 import render_jinja2
from utilities.querydict import dict_to_querydict
@@ -45,7 +44,7 @@ __all__ = (
)
class EventRule(CustomFieldsMixin, ExportTemplatesMixin, OwnerMixin, TagsMixin, ChangeLoggedModel):
class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
"""
An EventRule defines an action to be taken automatically in response to a specific set of events, such as when a
specific type of object is created, modified, or deleted. The action to be taken might entail transmitting a
@@ -156,7 +155,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, OwnerMixin, TagsMixin,
return False
class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, OwnerMixin, ChangeLoggedModel):
class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
"""
A Webhook defines a request that will be sent to a remote application when an object is created, updated, and/or
delete in NetBox. The request will contain a representation of the object, which the remote application can act on.
@@ -295,7 +294,7 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, OwnerMixin, Ch
return render_jinja2(self.payload_url, context)
class CustomLink(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
"""
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
code to be rendered with an object as context.
@@ -395,14 +394,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMod
}
class ExportTemplate(
SyncedDataMixin,
CloningMixin,
ExportTemplatesMixin,
OwnerMixin,
ChangeLoggedModel,
RenderTemplateMixin,
):
class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, RenderTemplateMixin):
object_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='export_templates',
@@ -464,7 +456,7 @@ class ExportTemplate(
return _context
class SavedFilter(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel):
class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
"""
A set of predefined keyword parameters that can be reused to filter for specific objects.
"""

View File

@@ -8,7 +8,6 @@ from taggit.models import TagBase, GenericTaggedItemBase
from netbox.choices import ColorChoices
from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin
from netbox.models.mixins import OwnerMixin
from utilities.fields import ColorField
from utilities.querysets import RestrictedQuerySet
@@ -22,7 +21,7 @@ __all__ = (
# Tags
#
class Tag(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel, TagBase):
class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
id = models.BigAutoField(
primary_key=True
)

View File

@@ -326,9 +326,6 @@ class BaseScript:
# Declare the placeholder for the current request
self.request = None
# Initiate the storage backend (local, S3, etc) as a class attr
self.storage = storages.create_storage(storages.backends["scripts"])
# Compile test methods and initialize results skeleton
for method in dir(self):
if method.startswith('test_') and callable(getattr(self, method)):
@@ -394,7 +391,8 @@ class BaseScript:
return inspect.getfile(self.__class__)
def findsource(self, object):
with self.storage.open(os.path.basename(self.filename), 'r') as f:
storage = storages.create_storage(storages.backends["scripts"])
with storage.open(os.path.basename(self.filename), 'r') as f:
data = f.read()
# Break the source code into lines

View File

@@ -10,7 +10,7 @@ from core.tables import JobTable
from core.models import Job
from netbox.constants import EMPTY_TABLE_TEXT
from netbox.events import get_event_text
from netbox.tables import BaseTable, NetBoxTable, PrimaryModelTable, columns
from netbox.tables import BaseTable, NetBoxTable, columns
from .columns import NotificationActionsColumn
__all__ = (
@@ -109,10 +109,6 @@ class CustomFieldTable(NetBoxTable):
validation_regex = tables.Column(
verbose_name=_('Validation Regex'),
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
class Meta(NetBoxTable.Meta):
model = CustomField
@@ -150,10 +146,6 @@ class CustomFieldChoiceSetTable(NetBoxTable):
verbose_name=_('Order Alphabetically'),
false_mark=None
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
class Meta(NetBoxTable.Meta):
model = CustomFieldChoiceSet
@@ -179,10 +171,6 @@ class CustomLinkTable(NetBoxTable):
verbose_name=_('New Window'),
false_mark=None
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
class Meta(NetBoxTable.Meta):
model = CustomLink
@@ -226,10 +214,6 @@ class ExportTemplateTable(NetBoxTable):
orderable=False,
verbose_name=_('Synced')
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
class Meta(NetBoxTable.Meta):
model = ExportTemplate
@@ -310,10 +294,6 @@ class SavedFilterTable(NetBoxTable):
verbose_name=_('Shared'),
false_mark=None
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
def value_parameters(self, value):
return json.dumps(value)
@@ -470,10 +450,6 @@ class WebhookTable(NetBoxTable):
ssl_validation = columns.BooleanColumn(
verbose_name=_('SSL Validation')
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
tags = columns.TagColumn(
url_name='extras:webhook_list'
)
@@ -512,10 +488,6 @@ class EventRuleTable(NetBoxTable):
func=get_event_text,
orderable=False
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
tags = columns.TagColumn(
url_name='extras:webhook_list'
)
@@ -542,10 +514,6 @@ class TagTable(NetBoxTable):
object_types = columns.ContentTypesColumn(
verbose_name=_('Object Types'),
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
class Meta(NetBoxTable.Meta):
model = Tag
@@ -579,7 +547,7 @@ class TaggedItemTable(NetBoxTable):
fields = ('id', 'content_type', 'content_object')
class ConfigContextProfileTable(PrimaryModelTable):
class ConfigContextProfileTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
@@ -600,7 +568,7 @@ class ConfigContextProfileTable(PrimaryModelTable):
url_name='extras:configcontextprofile_list'
)
class Meta(PrimaryModelTable.Meta):
class Meta(NetBoxTable.Meta):
model = ConfigContextProfile
fields = (
'pk', 'id', 'name', 'description', 'comments', 'data_source', 'data_file', 'is_synced', 'tags', 'created',
@@ -633,10 +601,6 @@ class ConfigContextTable(NetBoxTable):
orderable=False,
verbose_name=_('Synced')
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
tags = columns.TagColumn(
url_name='extras:configcontext_list'
)
@@ -681,10 +645,6 @@ class ConfigTemplateTable(NetBoxTable):
verbose_name=_('As Attachment'),
false_mark=None
)
owner = tables.Column(
linkify=True,
verbose_name=_('Owner')
)
tags = columns.TagColumn(
url_name='extras:configtemplate_list'
)

View File

@@ -3,7 +3,6 @@ import datetime
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from django.utils.timezone import make_aware, now
from rest_framework import status
from core.choices import ManagedFileRootPathChoices
from core.events import *
@@ -12,8 +11,7 @@ from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Loca
from extras.choices import *
from extras.models import *
from extras.scripts import BooleanVar, IntegerVar, Script as PythonClass, StringVar
from users.constants import TOKEN_PREFIX
from users.models import Group, Token, User
from users.models import Group, User
from utilities.testing import APITestCase, APIViewTestCases
@@ -856,47 +854,6 @@ class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
)
ConfigTemplate.objects.bulk_create(config_templates)
def test_render(self):
configtemplate = ConfigTemplate.objects.first()
self.add_permissions('extras.render_configtemplate', 'extras.view_configtemplate')
url = reverse('extras-api:configtemplate-render', kwargs={'pk': configtemplate.pk})
response = self.client.post(url, {'foo': 'bar'}, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(response.data['content'], 'Foo: bar')
def test_render_without_permission(self):
configtemplate = ConfigTemplate.objects.first()
# No permissions added - user has no render permission
url = reverse('extras-api:configtemplate-render', kwargs={'pk': configtemplate.pk})
response = self.client.post(url, {'foo': 'bar'}, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND)
def test_render_token_write_enabled(self):
configtemplate = ConfigTemplate.objects.first()
self.add_permissions('extras.render_configtemplate', 'extras.view_configtemplate')
url = reverse('extras-api:configtemplate-render', kwargs={'pk': configtemplate.pk})
# Request without token auth should fail with PermissionDenied
response = self.client.post(url, {'foo': 'bar'}, format='json')
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
# Create token with write_enabled=False
token = Token.objects.create(version=2, user=self.user, write_enabled=False)
token_header = f'Bearer {TOKEN_PREFIX}{token.key}.{token.token}'
# Request with write-disabled token should fail
response = self.client.post(url, {'foo': 'bar'}, format='json', HTTP_AUTHORIZATION=token_header)
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
# Enable write and retry
token.write_enabled = True
token.save()
response = self.client.post(url, {'foo': 'bar'}, format='json', HTTP_AUTHORIZATION=token_header)
self.assertHttpStatus(response, status.HTTP_200_OK)
class ScriptTest(APITestCase):

View File

@@ -2,7 +2,7 @@ from rest_framework import serializers
from ipam.models import ASN, ASNRange, RIR
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
__all__ = (
@@ -13,7 +13,7 @@ __all__ = (
)
class RIRSerializer(OrganizationalModelSerializer):
class RIRSerializer(NetBoxModelSerializer):
# Related object counts
aggregate_count = RelatedObjectCountField('aggregates')
@@ -21,13 +21,13 @@ class RIRSerializer(OrganizationalModelSerializer):
class Meta:
model = RIR
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'is_private', 'description', 'owner', 'tags',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'is_private', 'description', 'tags',
'custom_fields', 'created', 'last_updated', 'aggregate_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'aggregate_count')
class ASNRangeSerializer(OrganizationalModelSerializer):
class ASNRangeSerializer(NetBoxModelSerializer):
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
asn_count = serializers.IntegerField(read_only=True)
@@ -36,12 +36,12 @@ class ASNRangeSerializer(OrganizationalModelSerializer):
model = ASNRange
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'rir', 'start', 'end', 'tenant', 'description',
'owner', 'tags', 'custom_fields', 'created', 'last_updated', 'asn_count',
'tags', 'custom_fields', 'created', 'last_updated', 'asn_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ASNSerializer(PrimaryModelSerializer):
class ASNSerializer(NetBoxModelSerializer):
rir = RIRSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
@@ -52,7 +52,7 @@ class ASNSerializer(PrimaryModelSerializer):
class Meta:
model = ASN
fields = [
'id', 'url', 'display_url', 'display', 'asn', 'rir', 'tenant', 'description', 'owner', 'comments', 'tags',
'id', 'url', 'display_url', 'display', 'asn', 'rir', 'tenant', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'site_count', 'provider_count',
]
brief_fields = ('id', 'url', 'display', 'asn', 'description')

View File

@@ -4,7 +4,7 @@ from rest_framework import serializers
from ipam.models import FHRPGroup, FHRPGroupAssignment
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer, PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from utilities.api import get_serializer_for_model
from .ip import IPAddressSerializer
@@ -14,14 +14,14 @@ __all__ = (
)
class FHRPGroupSerializer(PrimaryModelSerializer):
class FHRPGroupSerializer(NetBoxModelSerializer):
ip_addresses = IPAddressSerializer(nested=True, many=True, read_only=True)
class Meta:
model = FHRPGroup
fields = [
'id', 'name', 'url', 'display_url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key',
'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
]
brief_fields = ('id', 'url', 'display', 'protocol', 'group_id', 'description')

View File

@@ -7,7 +7,7 @@ from ipam.choices import *
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS
from ipam.models import Aggregate, IPAddress, IPRange, Prefix
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from .asns import RIRSerializer
@@ -28,7 +28,7 @@ __all__ = (
)
class AggregateSerializer(PrimaryModelSerializer):
class AggregateSerializer(NetBoxModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
@@ -38,12 +38,12 @@ class AggregateSerializer(PrimaryModelSerializer):
model = Aggregate
fields = [
'id', 'url', 'display_url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description',
'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description')
class PrefixSerializer(PrimaryModelSerializer):
class PrefixSerializer(NetBoxModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
scope_type = ContentTypeField(
@@ -68,7 +68,7 @@ class PrefixSerializer(PrimaryModelSerializer):
model = Prefix
fields = [
'id', 'url', 'display_url', 'display', 'family', 'prefix', 'vrf', 'scope_type', 'scope_id', 'scope',
'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'owner', 'comments', 'tags',
'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated', 'children', '_depth',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description', '_depth')
@@ -133,7 +133,7 @@ class AvailablePrefixSerializer(serializers.Serializer):
# IP ranges
#
class IPRangeSerializer(PrimaryModelSerializer):
class IPRangeSerializer(NetBoxModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
start_address = IPAddressField()
end_address = IPAddressField()
@@ -146,7 +146,7 @@ class IPRangeSerializer(PrimaryModelSerializer):
model = IPRange
fields = [
'id', 'url', 'display_url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant',
'status', 'role', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'status', 'role', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'mark_populated', 'mark_utilized',
]
brief_fields = ('id', 'url', 'display', 'family', 'start_address', 'end_address', 'description')
@@ -156,7 +156,7 @@ class IPRangeSerializer(PrimaryModelSerializer):
# IP addresses
#
class IPAddressSerializer(PrimaryModelSerializer):
class IPAddressSerializer(NetBoxModelSerializer):
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
address = IPAddressField()
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
@@ -177,7 +177,7 @@ class IPAddressSerializer(PrimaryModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role',
'assigned_object_type', 'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside',
'dns_name', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'dns_name', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'address', 'description')

View File

@@ -1,13 +1,13 @@
from ipam.models import Role
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import OrganizationalModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
__all__ = (
'RoleSerializer',
)
class RoleSerializer(OrganizationalModelSerializer):
class RoleSerializer(NetBoxModelSerializer):
# Related object counts
prefix_count = RelatedObjectCountField('prefixes')
@@ -16,7 +16,7 @@ class RoleSerializer(OrganizationalModelSerializer):
class Meta:
model = Role
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'weight', 'description', 'owner', 'tags',
'custom_fields', 'created', 'last_updated', 'prefix_count', 'vlan_count',
'id', 'url', 'display_url', 'display', 'name', 'slug', 'weight', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'prefix_count', 'vlan_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'prefix_count', 'vlan_count')

View File

@@ -6,7 +6,7 @@ from ipam.choices import *
from ipam.constants import SERVICE_ASSIGNMENT_MODELS
from ipam.models import IPAddress, Service, ServiceTemplate
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from utilities.api import get_serializer_for_model
from .ip import IPAddressSerializer
@@ -16,19 +16,19 @@ __all__ = (
)
class ServiceTemplateSerializer(PrimaryModelSerializer):
class ServiceTemplateSerializer(NetBoxModelSerializer):
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
class Meta:
model = ServiceTemplate
fields = [
'id', 'url', 'display_url', 'display', 'name', 'protocol', 'ports', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'name', 'protocol', 'ports', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')
class ServiceSerializer(PrimaryModelSerializer):
class ServiceSerializer(NetBoxModelSerializer):
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
ipaddresses = SerializedPKRelatedField(
queryset=IPAddress.objects.all(),
@@ -46,7 +46,7 @@ class ServiceSerializer(PrimaryModelSerializer):
model = Service
fields = [
'id', 'url', 'display_url', 'display', 'parent_object_type', 'parent_object_id', 'parent', 'name',
'protocol', 'ports', 'ipaddresses', 'description', 'owner', 'comments', 'tags', 'custom_fields',
'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')

View File

@@ -7,7 +7,7 @@ from ipam.choices import *
from ipam.constants import VLANGROUP_SCOPE_TYPES
from ipam.models import VLAN, VLANGroup, VLANTranslationPolicy, VLANTranslationRule
from netbox.api.fields import ChoiceField, ContentTypeField, IntegerRangeSerializer, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
@@ -24,7 +24,7 @@ __all__ = (
)
class VLANGroupSerializer(OrganizationalModelSerializer):
class VLANGroupSerializer(NetBoxModelSerializer):
scope_type = ContentTypeField(
queryset=ContentType.objects.filter(
model__in=VLANGROUP_SCOPE_TYPES
@@ -46,8 +46,7 @@ class VLANGroupSerializer(OrganizationalModelSerializer):
model = VLANGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'vid_ranges',
'tenant', 'description', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count',
'utilization',
'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count')
validators = []
@@ -61,7 +60,7 @@ class VLANGroupSerializer(OrganizationalModelSerializer):
return serializer(obj.scope, nested=True, context=context).data
class VLANSerializer(PrimaryModelSerializer):
class VLANSerializer(NetBoxModelSerializer):
site = SiteSerializer(nested=True, required=False, allow_null=True)
group = VLANGroupSerializer(nested=True, required=False, allow_null=True, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
@@ -78,7 +77,7 @@ class VLANSerializer(PrimaryModelSerializer):
model = VLAN
fields = [
'id', 'url', 'display_url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role',
'description', 'qinq_role', 'qinq_svlan', 'owner', 'comments', 'l2vpn_termination', 'tags', 'custom_fields',
'description', 'qinq_role', 'qinq_svlan', 'comments', 'l2vpn_termination', 'tags', 'custom_fields',
'created', 'last_updated', 'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'vid', 'name', 'description')
@@ -126,10 +125,10 @@ class VLANTranslationRuleSerializer(NetBoxModelSerializer):
fields = ['id', 'url', 'display', 'policy', 'local_vid', 'remote_vid', 'description']
class VLANTranslationPolicySerializer(PrimaryModelSerializer):
class VLANTranslationPolicySerializer(NetBoxModelSerializer):
rules = VLANTranslationRuleSerializer(many=True, read_only=True)
class Meta:
model = VLANTranslationPolicy
fields = ['id', 'url', 'display', 'name', 'description', 'display', 'rules', 'owner', 'comments']
fields = ['id', 'url', 'display', 'name', 'description', 'display', 'rules']
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -1,6 +1,6 @@
from ipam.models import RouteTarget, VRF
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import PrimaryModelSerializer
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
__all__ = (
@@ -9,19 +9,19 @@ __all__ = (
)
class RouteTargetSerializer(PrimaryModelSerializer):
class RouteTargetSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = RouteTarget
fields = [
'id', 'url', 'display_url', 'display', 'name', 'tenant', 'description', 'owner', 'comments', 'tags',
'id', 'url', 'display_url', 'display', 'name', 'tenant', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class VRFSerializer(PrimaryModelSerializer):
class VRFSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
@@ -43,8 +43,8 @@ class VRFSerializer(PrimaryModelSerializer):
class Meta:
model = VRF
fields = [
'id', 'url', 'display_url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'owner',
'comments', 'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated',
'ipaddress_count', 'prefix_count',
'id', 'url', 'display_url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments',
'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated', 'ipaddress_count',
'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'rd', 'description', 'prefix_count')

View File

@@ -11,9 +11,7 @@ from netaddr.core import AddrFormatError
from circuits.models import Provider
from dcim.models import Device, Interface, Region, Site, SiteGroup
from netbox.filtersets import (
ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet, PrimaryModelFilterSet,
)
from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import (
@@ -47,7 +45,7 @@ __all__ = (
)
class VRFFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
class VRFFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
import_target_id = django_filters.ModelMultipleChoiceFilter(
field_name='import_targets',
queryset=RouteTarget.objects.all(),
@@ -85,7 +83,7 @@ class VRFFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
fields = ('id', 'name', 'rd', 'enforce_unique', 'description')
class RouteTargetFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
class RouteTargetFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
importing_vrf_id = django_filters.ModelMultipleChoiceFilter(
field_name='importing_vrfs',
queryset=VRF.objects.all(),
@@ -151,7 +149,7 @@ class RIRFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'is_private', 'description')
class AggregateFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
class AggregateFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='prefix',
lookup_expr='family'
@@ -223,7 +221,7 @@ class ASNRangeFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
)
class ASNFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
class ASNFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
rir_id = django_filters.ModelMultipleChoiceFilter(
queryset=RIR.objects.all(),
label=_('RIR (ID)'),
@@ -292,7 +290,7 @@ class RoleFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'description', 'weight')
class PrefixFilterSet(PrimaryModelFilterSet, ScopedFilterSet, TenancyFilterSet, ContactModelFilterSet):
class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='prefix',
lookup_expr='family'
@@ -458,7 +456,7 @@ class PrefixFilterSet(PrimaryModelFilterSet, ScopedFilterSet, TenancyFilterSet,
).distinct()
class IPRangeFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='start_address',
lookup_expr='family'
@@ -550,7 +548,7 @@ class IPRangeFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilt
return queryset.filter(q)
class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
family = django_filters.NumberFilter(
field_name='address',
lookup_expr='family'
@@ -597,7 +595,6 @@ class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFi
to_field_name='rd',
label=_('VRF (RD)'),
)
assigned_object_type = ContentTypeFilter()
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
@@ -786,7 +783,7 @@ class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFi
)
class FHRPGroupFilterSet(PrimaryModelFilterSet):
class FHRPGroupFilterSet(NetBoxModelFilterSet):
protocol = django_filters.MultipleChoiceFilter(
choices=FHRPGroupProtocolChoices
)
@@ -936,7 +933,7 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
)
class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -1087,7 +1084,7 @@ class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
).distinct()
class VLANTranslationPolicyFilterSet(PrimaryModelFilterSet):
class VLANTranslationPolicyFilterSet(NetBoxModelFilterSet):
class Meta:
model = VLANTranslationPolicy
@@ -1134,7 +1131,7 @@ class VLANTranslationRuleFilterSet(NetBoxModelFilterSet):
return queryset.filter(qs_filter)
class ServiceTemplateFilterSet(PrimaryModelFilterSet):
class ServiceTemplateFilterSet(NetBoxModelFilterSet):
port = NumericArrayFilter(
field_name='ports',
lookup_expr='contains'
@@ -1154,8 +1151,7 @@ class ServiceTemplateFilterSet(PrimaryModelFilterSet):
return queryset.filter(qs_filter)
class ServiceFilterSet(ContactModelFilterSet, PrimaryModelFilterSet):
parent_object_type = ContentTypeFilter()
class ServiceFilterSet(ContactModelFilterSet, NetBoxModelFilterSet):
device = MultiValueCharFilter(
method='filter_device',
field_name='name',

Some files were not shown because too many files have changed in this diff Show More