mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-19 09:53:34 -06:00
Add ModuleBay and ModuleBayTemplate models
This commit is contained in:
parent
5f9f0e3ed3
commit
e529d7fd3b
@ -20,6 +20,8 @@ __all__ = [
|
|||||||
'NestedInterfaceTemplateSerializer',
|
'NestedInterfaceTemplateSerializer',
|
||||||
'NestedInventoryItemSerializer',
|
'NestedInventoryItemSerializer',
|
||||||
'NestedManufacturerSerializer',
|
'NestedManufacturerSerializer',
|
||||||
|
'NestedModuleBaySerializer',
|
||||||
|
'NestedModuleBayTemplateSerializer',
|
||||||
'NestedPlatformSerializer',
|
'NestedPlatformSerializer',
|
||||||
'NestedPowerFeedSerializer',
|
'NestedPowerFeedSerializer',
|
||||||
'NestedPowerOutletSerializer',
|
'NestedPowerOutletSerializer',
|
||||||
@ -195,6 +197,14 @@ class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'display', 'name']
|
fields = ['id', 'url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
|
class NestedModuleBayTemplateSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebaytemplate-detail')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ModuleBayTemplate
|
||||||
|
fields = ['id', 'url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceBayTemplateSerializer(WritableNestedSerializer):
|
class NestedDeviceBayTemplateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
|
||||||
|
|
||||||
@ -298,6 +308,15 @@ class NestedFrontPortSerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
fields = ['id', 'url', 'display', 'device', 'name', 'cable', '_occupied']
|
||||||
|
|
||||||
|
|
||||||
|
class NestedModuleBaySerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
||||||
|
# module = NestedModuleSerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.DeviceBay
|
||||||
|
fields = ['id', 'url', 'display', 'name']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
||||||
device = NestedDeviceSerializer(read_only=True)
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
|
@ -409,6 +409,15 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateSerializer(ValidatedModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebaytemplate-detail')
|
||||||
|
device_type = NestedDeviceTypeSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
fields = ['id', 'url', 'display', 'device_type', 'name', 'label', 'description', 'created', 'last_updated']
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
@ -707,6 +716,19 @@ class FrontPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBaySerializer(PrimaryModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
||||||
|
device = NestedDeviceSerializer()
|
||||||
|
# installed_module = NestedModuleSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBay
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'name', 'label', 'description', 'tags', 'custom_fields', 'created',
|
||||||
|
'last_updated',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceBaySerializer(PrimaryModelSerializer):
|
class DeviceBaySerializer(PrimaryModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
|
@ -28,6 +28,7 @@ router.register('power-outlet-templates', views.PowerOutletTemplateViewSet)
|
|||||||
router.register('interface-templates', views.InterfaceTemplateViewSet)
|
router.register('interface-templates', views.InterfaceTemplateViewSet)
|
||||||
router.register('front-port-templates', views.FrontPortTemplateViewSet)
|
router.register('front-port-templates', views.FrontPortTemplateViewSet)
|
||||||
router.register('rear-port-templates', views.RearPortTemplateViewSet)
|
router.register('rear-port-templates', views.RearPortTemplateViewSet)
|
||||||
|
router.register('module-bay-templates', views.ModuleBayTemplateViewSet)
|
||||||
router.register('device-bay-templates', views.DeviceBayTemplateViewSet)
|
router.register('device-bay-templates', views.DeviceBayTemplateViewSet)
|
||||||
|
|
||||||
# Devices
|
# Devices
|
||||||
@ -43,6 +44,7 @@ router.register('power-outlets', views.PowerOutletViewSet)
|
|||||||
router.register('interfaces', views.InterfaceViewSet)
|
router.register('interfaces', views.InterfaceViewSet)
|
||||||
router.register('front-ports', views.FrontPortViewSet)
|
router.register('front-ports', views.FrontPortViewSet)
|
||||||
router.register('rear-ports', views.RearPortViewSet)
|
router.register('rear-ports', views.RearPortViewSet)
|
||||||
|
router.register('module-bays', views.ModuleBayViewSet)
|
||||||
router.register('device-bays', views.DeviceBayViewSet)
|
router.register('device-bays', views.DeviceBayViewSet)
|
||||||
router.register('inventory-items', views.InventoryItemViewSet)
|
router.register('inventory-items', views.InventoryItemViewSet)
|
||||||
|
|
||||||
|
@ -329,6 +329,12 @@ class RearPortTemplateViewSet(ModelViewSet):
|
|||||||
filterset_class = filtersets.RearPortTemplateFilterSet
|
filterset_class = filtersets.RearPortTemplateFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateViewSet(ModelViewSet):
|
||||||
|
queryset = ModuleBayTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||||
|
serializer_class = serializers.ModuleBayTemplateSerializer
|
||||||
|
filterset_class = filtersets.ModuleBayTemplateFilterSet
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateViewSet(ModelViewSet):
|
class DeviceBayTemplateViewSet(ModelViewSet):
|
||||||
queryset = DeviceBayTemplate.objects.prefetch_related('device_type__manufacturer')
|
queryset = DeviceBayTemplate.objects.prefetch_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.DeviceBayTemplateSerializer
|
serializer_class = serializers.DeviceBayTemplateSerializer
|
||||||
@ -569,15 +575,22 @@ class RearPortViewSet(PassThroughPortMixin, ModelViewSet):
|
|||||||
brief_prefetch_fields = ['device']
|
brief_prefetch_fields = ['device']
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayViewSet(ModelViewSet):
|
||||||
|
queryset = ModuleBay.objects.prefetch_related('tags')
|
||||||
|
serializer_class = serializers.ModuleBaySerializer
|
||||||
|
filterset_class = filtersets.ModuleBayFilterSet
|
||||||
|
brief_prefetch_fields = ['device']
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayViewSet(ModelViewSet):
|
class DeviceBayViewSet(ModelViewSet):
|
||||||
queryset = DeviceBay.objects.prefetch_related('installed_device').prefetch_related('tags')
|
queryset = DeviceBay.objects.prefetch_related('installed_device', 'tags')
|
||||||
serializer_class = serializers.DeviceBaySerializer
|
serializer_class = serializers.DeviceBaySerializer
|
||||||
filterset_class = filtersets.DeviceBayFilterSet
|
filterset_class = filtersets.DeviceBayFilterSet
|
||||||
brief_prefetch_fields = ['device']
|
brief_prefetch_fields = ['device']
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemViewSet(ModelViewSet):
|
class InventoryItemViewSet(ModelViewSet):
|
||||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer').prefetch_related('tags')
|
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer', 'tags')
|
||||||
serializer_class = serializers.InventoryItemSerializer
|
serializer_class = serializers.InventoryItemSerializer
|
||||||
filterset_class = filtersets.InventoryItemFilterSet
|
filterset_class = filtersets.InventoryItemFilterSet
|
||||||
brief_prefetch_fields = ['device']
|
brief_prefetch_fields = ['device']
|
||||||
|
@ -41,6 +41,8 @@ __all__ = (
|
|||||||
'InventoryItemFilterSet',
|
'InventoryItemFilterSet',
|
||||||
'LocationFilterSet',
|
'LocationFilterSet',
|
||||||
'ManufacturerFilterSet',
|
'ManufacturerFilterSet',
|
||||||
|
'ModuleBayFilterSet',
|
||||||
|
'ModuleBayTemplateFilterSet',
|
||||||
'PathEndpointFilterSet',
|
'PathEndpointFilterSet',
|
||||||
'PlatformFilterSet',
|
'PlatformFilterSet',
|
||||||
'PowerConnectionFilterSet',
|
'PowerConnectionFilterSet',
|
||||||
@ -447,6 +449,10 @@ class DeviceTypeFilterSet(PrimaryModelFilterSet):
|
|||||||
method='_pass_through_ports',
|
method='_pass_through_ports',
|
||||||
label='Has pass-through ports',
|
label='Has pass-through ports',
|
||||||
)
|
)
|
||||||
|
module_bays = django_filters.BooleanFilter(
|
||||||
|
method='_module_bays',
|
||||||
|
label='Has module bays',
|
||||||
|
)
|
||||||
device_bays = django_filters.BooleanFilter(
|
device_bays = django_filters.BooleanFilter(
|
||||||
method='_device_bays',
|
method='_device_bays',
|
||||||
label='Has device bays',
|
label='Has device bays',
|
||||||
@ -490,6 +496,9 @@ class DeviceTypeFilterSet(PrimaryModelFilterSet):
|
|||||||
rearporttemplates__isnull=value
|
rearporttemplates__isnull=value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _module_bays(self, queryset, name, value):
|
||||||
|
return queryset.exclude(modulebaytemplates__isnull=value)
|
||||||
|
|
||||||
def _device_bays(self, queryset, name, value):
|
def _device_bays(self, queryset, name, value):
|
||||||
return queryset.exclude(devicebaytemplates__isnull=value)
|
return queryset.exclude(devicebaytemplates__isnull=value)
|
||||||
|
|
||||||
@ -576,6 +585,13 @@ class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentF
|
|||||||
fields = ['id', 'name', 'type', 'color', 'positions']
|
fields = ['id', 'name', 'type', 'color', 'positions']
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
fields = ['id', 'name']
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
|
class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -760,6 +776,10 @@ class DeviceFilterSet(PrimaryModelFilterSet, TenancyFilterSet, LocalConfigContex
|
|||||||
method='_pass_through_ports',
|
method='_pass_through_ports',
|
||||||
label='Has pass-through ports',
|
label='Has pass-through ports',
|
||||||
)
|
)
|
||||||
|
module_bays = django_filters.BooleanFilter(
|
||||||
|
method='_module_bays',
|
||||||
|
label='Has module bays',
|
||||||
|
)
|
||||||
device_bays = django_filters.BooleanFilter(
|
device_bays = django_filters.BooleanFilter(
|
||||||
method='_device_bays',
|
method='_device_bays',
|
||||||
label='Has device bays',
|
label='Has device bays',
|
||||||
@ -811,6 +831,9 @@ class DeviceFilterSet(PrimaryModelFilterSet, TenancyFilterSet, LocalConfigContex
|
|||||||
rearports__isnull=value
|
rearports__isnull=value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _module_bays(self, queryset, name, value):
|
||||||
|
return queryset.exclude(modulebays__isnull=value)
|
||||||
|
|
||||||
def _device_bays(self, queryset, name, value):
|
def _device_bays(self, queryset, name, value):
|
||||||
return queryset.exclude(devicebays__isnull=value)
|
return queryset.exclude(devicebays__isnull=value)
|
||||||
|
|
||||||
@ -1104,6 +1127,13 @@ class RearPortFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableTe
|
|||||||
fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description']
|
fields = ['id', 'name', 'label', 'type', 'color', 'positions', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBay
|
||||||
|
fields = ['id', 'name', 'label', 'description']
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
|
class DeviceBayFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -13,6 +13,7 @@ __all__ = (
|
|||||||
# 'FrontPortBulkCreateForm',
|
# 'FrontPortBulkCreateForm',
|
||||||
'InterfaceBulkCreateForm',
|
'InterfaceBulkCreateForm',
|
||||||
'InventoryItemBulkCreateForm',
|
'InventoryItemBulkCreateForm',
|
||||||
|
'ModuleBayBulkCreateForm',
|
||||||
'PowerOutletBulkCreateForm',
|
'PowerOutletBulkCreateForm',
|
||||||
'PowerPortBulkCreateForm',
|
'PowerPortBulkCreateForm',
|
||||||
'RearPortBulkCreateForm',
|
'RearPortBulkCreateForm',
|
||||||
@ -95,6 +96,11 @@ class RearPortBulkCreateForm(
|
|||||||
field_order = ('name_pattern', 'label_pattern', 'type', 'positions', 'mark_connected', 'description', 'tags')
|
field_order = ('name_pattern', 'label_pattern', 'type', 'positions', 'mark_connected', 'description', 'tags')
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayBulkCreateForm(DeviceBulkAddComponentForm):
|
||||||
|
model = ModuleBay
|
||||||
|
field_order = ('name_pattern', 'label_pattern', 'description', 'tags')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm):
|
class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
field_order = ('name_pattern', 'label_pattern', 'description', 'tags')
|
field_order = ('name_pattern', 'label_pattern', 'description', 'tags')
|
||||||
|
@ -7,7 +7,6 @@ from dcim.choices import *
|
|||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
||||||
from ipam.constants import BGP_ASN_MIN, BGP_ASN_MAX
|
|
||||||
from ipam.models import VLAN, ASN
|
from ipam.models import VLAN, ASN
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
@ -33,6 +32,8 @@ __all__ = (
|
|||||||
'InventoryItemBulkEditForm',
|
'InventoryItemBulkEditForm',
|
||||||
'LocationBulkEditForm',
|
'LocationBulkEditForm',
|
||||||
'ManufacturerBulkEditForm',
|
'ManufacturerBulkEditForm',
|
||||||
|
'ModuleBayBulkEditForm',
|
||||||
|
'ModuleBayTemplateBulkEditForm',
|
||||||
'PlatformBulkEditForm',
|
'PlatformBulkEditForm',
|
||||||
'PowerFeedBulkEditForm',
|
'PowerFeedBulkEditForm',
|
||||||
'PowerOutletBulkEditForm',
|
'PowerOutletBulkEditForm',
|
||||||
@ -823,6 +824,23 @@ class RearPortTemplateBulkEditForm(BulkEditForm):
|
|||||||
nullable_fields = ('description',)
|
nullable_fields = ('description',)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateBulkEditForm(BulkEditForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=ModuleBayTemplate.objects.all(),
|
||||||
|
widget=forms.MultipleHiddenInput()
|
||||||
|
)
|
||||||
|
label = forms.CharField(
|
||||||
|
max_length=64,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
description = forms.CharField(
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = ('label', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateBulkEditForm(BulkEditForm):
|
class DeviceBayTemplateBulkEditForm(BulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=DeviceBayTemplate.objects.all(),
|
queryset=DeviceBayTemplate.objects.all(),
|
||||||
@ -1076,6 +1094,20 @@ class RearPortBulkEditForm(
|
|||||||
nullable_fields = ['label', 'description']
|
nullable_fields = ['label', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayBulkEditForm(
|
||||||
|
form_from_model(DeviceBay, ['label', 'description']),
|
||||||
|
AddRemoveTagsForm,
|
||||||
|
CustomFieldModelBulkEditForm
|
||||||
|
):
|
||||||
|
pk = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=ModuleBay.objects.all(),
|
||||||
|
widget=forms.MultipleHiddenInput()
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = ['label', 'description']
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayBulkEditForm(
|
class DeviceBayBulkEditForm(
|
||||||
form_from_model(DeviceBay, ['label', 'description']),
|
form_from_model(DeviceBay, ['label', 'description']),
|
||||||
AddRemoveTagsForm,
|
AddRemoveTagsForm,
|
||||||
|
@ -26,6 +26,7 @@ __all__ = (
|
|||||||
'InventoryItemCSVForm',
|
'InventoryItemCSVForm',
|
||||||
'LocationCSVForm',
|
'LocationCSVForm',
|
||||||
'ManufacturerCSVForm',
|
'ManufacturerCSVForm',
|
||||||
|
'ModuleBayCSVForm',
|
||||||
'PlatformCSVForm',
|
'PlatformCSVForm',
|
||||||
'PowerFeedCSVForm',
|
'PowerFeedCSVForm',
|
||||||
'PowerOutletCSVForm',
|
'PowerOutletCSVForm',
|
||||||
@ -678,6 +679,17 @@ class RearPortCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayCSVForm(CustomFieldModelCSVForm):
|
||||||
|
device = CSVModelChoiceField(
|
||||||
|
queryset=Device.objects.all(),
|
||||||
|
to_field_name='name'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBay
|
||||||
|
fields = ('device', 'name', 'label', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayCSVForm(CustomFieldModelCSVForm):
|
class DeviceBayCSVForm(CustomFieldModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
|
@ -29,6 +29,7 @@ __all__ = (
|
|||||||
'InventoryItemFilterForm',
|
'InventoryItemFilterForm',
|
||||||
'LocationFilterForm',
|
'LocationFilterForm',
|
||||||
'ManufacturerFilterForm',
|
'ManufacturerFilterForm',
|
||||||
|
'ModuleBayFilterForm',
|
||||||
'PlatformFilterForm',
|
'PlatformFilterForm',
|
||||||
'PowerConnectionFilterForm',
|
'PowerConnectionFilterForm',
|
||||||
'PowerFeedFilterForm',
|
'PowerFeedFilterForm',
|
||||||
@ -970,6 +971,16 @@ class RearPortFilterForm(DeviceComponentFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayFilterForm(DeviceComponentFilterForm):
|
||||||
|
model = ModuleBay
|
||||||
|
field_groups = [
|
||||||
|
['q', 'tag'],
|
||||||
|
['name', 'label'],
|
||||||
|
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
||||||
|
]
|
||||||
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayFilterForm(DeviceComponentFilterForm):
|
class DeviceBayFilterForm(DeviceComponentFilterForm):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
field_groups = [
|
field_groups = [
|
||||||
|
@ -39,6 +39,8 @@ __all__ = (
|
|||||||
'InventoryItemForm',
|
'InventoryItemForm',
|
||||||
'LocationForm',
|
'LocationForm',
|
||||||
'ManufacturerForm',
|
'ManufacturerForm',
|
||||||
|
'ModuleBayForm',
|
||||||
|
'ModuleBayTemplateForm',
|
||||||
'PlatformForm',
|
'PlatformForm',
|
||||||
'PopulateDeviceBayForm',
|
'PopulateDeviceBayForm',
|
||||||
'PowerFeedForm',
|
'PowerFeedForm',
|
||||||
@ -984,6 +986,17 @@ class RearPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
fields = [
|
||||||
|
'device_type', 'name', 'label', 'description',
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'device_type': forms.HiddenInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm):
|
class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceBayTemplate
|
model = DeviceBayTemplate
|
||||||
@ -1222,6 +1235,22 @@ class RearPortForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayForm(CustomFieldModelForm):
|
||||||
|
tags = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Tag.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBay
|
||||||
|
fields = [
|
||||||
|
'device', 'name', 'label', 'description', 'tags',
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'device': forms.HiddenInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayForm(CustomFieldModelForm):
|
class DeviceBayForm(CustomFieldModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
|
@ -25,6 +25,8 @@ __all__ = (
|
|||||||
'InterfaceCreateForm',
|
'InterfaceCreateForm',
|
||||||
'InterfaceTemplateCreateForm',
|
'InterfaceTemplateCreateForm',
|
||||||
'InventoryItemCreateForm',
|
'InventoryItemCreateForm',
|
||||||
|
'ModuleBayCreateForm',
|
||||||
|
'ModuleBayTemplateCreateForm',
|
||||||
'PowerOutletCreateForm',
|
'PowerOutletCreateForm',
|
||||||
'PowerOutletTemplateCreateForm',
|
'PowerOutletTemplateCreateForm',
|
||||||
'PowerPortCreateForm',
|
'PowerPortCreateForm',
|
||||||
@ -327,6 +329,10 @@ class RearPortTemplateCreateForm(ComponentTemplateCreateForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm):
|
||||||
|
field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateCreateForm(ComponentTemplateCreateForm):
|
class DeviceBayTemplateCreateForm(ComponentTemplateCreateForm):
|
||||||
field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')
|
field_order = ('manufacturer', 'device_type', 'name_pattern', 'label_pattern', 'description')
|
||||||
|
|
||||||
@ -619,6 +625,11 @@ class RearPortCreateForm(ComponentCreateForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayCreateForm(ComponentCreateForm):
|
||||||
|
model = ModuleBay
|
||||||
|
field_order = ('device', 'name_pattern', 'label_pattern', 'description', 'tags')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayCreateForm(ComponentCreateForm):
|
class DeviceBayCreateForm(ComponentCreateForm):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
field_order = ('device', 'name_pattern', 'label_pattern', 'description', 'tags')
|
field_order = ('device', 'name_pattern', 'label_pattern', 'description', 'tags')
|
||||||
|
@ -11,6 +11,7 @@ __all__ = (
|
|||||||
'DeviceTypeImportForm',
|
'DeviceTypeImportForm',
|
||||||
'FrontPortTemplateImportForm',
|
'FrontPortTemplateImportForm',
|
||||||
'InterfaceTemplateImportForm',
|
'InterfaceTemplateImportForm',
|
||||||
|
'ModuleBayTemplateImportForm',
|
||||||
'PowerOutletTemplateImportForm',
|
'PowerOutletTemplateImportForm',
|
||||||
'PowerPortTemplateImportForm',
|
'PowerPortTemplateImportForm',
|
||||||
'RearPortTemplateImportForm',
|
'RearPortTemplateImportForm',
|
||||||
@ -139,6 +140,15 @@ class RearPortTemplateImportForm(ComponentTemplateImportForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateImportForm(ComponentTemplateImportForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
fields = [
|
||||||
|
'device_type', 'name', 'label', 'description',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateImportForm(ComponentTemplateImportForm):
|
class DeviceBayTemplateImportForm(ComponentTemplateImportForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -56,6 +56,12 @@ class DCIMQuery(graphene.ObjectType):
|
|||||||
manufacturer = ObjectField(ManufacturerType)
|
manufacturer = ObjectField(ManufacturerType)
|
||||||
manufacturer_list = ObjectListField(ManufacturerType)
|
manufacturer_list = ObjectListField(ManufacturerType)
|
||||||
|
|
||||||
|
module_bay = ObjectField(ModuleBayType)
|
||||||
|
module_bay_list = ObjectListField(ModuleBayType)
|
||||||
|
|
||||||
|
module_bay_template = ObjectField(ModuleBayTemplateType)
|
||||||
|
module_bay_template_list = ObjectListField(ModuleBayTemplateType)
|
||||||
|
|
||||||
platform = ObjectField(PlatformType)
|
platform = ObjectField(PlatformType)
|
||||||
platform_list = ObjectListField(PlatformType)
|
platform_list = ObjectListField(PlatformType)
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ __all__ = (
|
|||||||
'InventoryItemType',
|
'InventoryItemType',
|
||||||
'LocationType',
|
'LocationType',
|
||||||
'ManufacturerType',
|
'ManufacturerType',
|
||||||
|
'ModuleBayType',
|
||||||
|
'ModuleBayTemplateType',
|
||||||
'PlatformType',
|
'PlatformType',
|
||||||
'PowerFeedType',
|
'PowerFeedType',
|
||||||
'PowerOutletType',
|
'PowerOutletType',
|
||||||
@ -254,6 +256,22 @@ class ManufacturerType(OrganizationalObjectType):
|
|||||||
filterset_class = filtersets.ManufacturerFilterSet
|
filterset_class = filtersets.ManufacturerFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayType(ComponentObjectType):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ModuleBay
|
||||||
|
fields = '__all__'
|
||||||
|
filterset_class = filtersets.ModuleBayFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateType(ComponentTemplateObjectType):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ModuleBayTemplate
|
||||||
|
fields = '__all__'
|
||||||
|
filterset_class = filtersets.ModuleBayTemplateFilterSet
|
||||||
|
|
||||||
|
|
||||||
class PlatformType(OrganizationalObjectType):
|
class PlatformType(OrganizationalObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
53
netbox/dcim/migrations/0145_modules.py
Normal file
53
netbox/dcim/migrations/0145_modules.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import django.core.serializers.json
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import taggit.managers
|
||||||
|
import utilities.fields
|
||||||
|
import utilities.ordering
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('extras', '0066_customfield_name_validation'),
|
||||||
|
('dcim', '0144_site_remove_deprecated_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ModuleBayTemplate',
|
||||||
|
fields=[
|
||||||
|
('created', models.DateField(auto_now_add=True, null=True)),
|
||||||
|
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=64)),
|
||||||
|
('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
|
||||||
|
('label', models.CharField(blank=True, max_length=64)),
|
||||||
|
('description', models.CharField(blank=True, max_length=200)),
|
||||||
|
('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modulebaytemplates', to='dcim.devicetype')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ('device_type', '_name'),
|
||||||
|
'unique_together': {('device_type', 'name')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ModuleBay',
|
||||||
|
fields=[
|
||||||
|
('created', models.DateField(auto_now_add=True, null=True)),
|
||||||
|
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||||
|
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=64)),
|
||||||
|
('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
|
||||||
|
('label', models.CharField(blank=True, max_length=64)),
|
||||||
|
('description', models.CharField(blank=True, max_length=200)),
|
||||||
|
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modulebays', to='dcim.device')),
|
||||||
|
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ('device', '_name'),
|
||||||
|
'unique_together': {('device', 'name')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -27,6 +27,8 @@ __all__ = (
|
|||||||
'InventoryItem',
|
'InventoryItem',
|
||||||
'Location',
|
'Location',
|
||||||
'Manufacturer',
|
'Manufacturer',
|
||||||
|
'ModuleBay',
|
||||||
|
'ModuleBayTemplate',
|
||||||
'Platform',
|
'Platform',
|
||||||
'PowerFeed',
|
'PowerFeed',
|
||||||
'PowerOutlet',
|
'PowerOutlet',
|
||||||
|
@ -9,7 +9,7 @@ from netbox.models import ChangeLoggedModel
|
|||||||
from utilities.fields import ColorField, NaturalOrderingField
|
from utilities.fields import ColorField, NaturalOrderingField
|
||||||
from utilities.ordering import naturalize_interface
|
from utilities.ordering import naturalize_interface
|
||||||
from .device_components import (
|
from .device_components import (
|
||||||
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, PowerOutlet, PowerPort, RearPort,
|
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, ModuleBay, PowerOutlet, PowerPort, RearPort,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ __all__ = (
|
|||||||
'DeviceBayTemplate',
|
'DeviceBayTemplate',
|
||||||
'FrontPortTemplate',
|
'FrontPortTemplate',
|
||||||
'InterfaceTemplate',
|
'InterfaceTemplate',
|
||||||
|
'ModuleBayTemplate',
|
||||||
'PowerOutletTemplate',
|
'PowerOutletTemplate',
|
||||||
'PowerPortTemplate',
|
'PowerPortTemplate',
|
||||||
'RearPortTemplate',
|
'RearPortTemplate',
|
||||||
@ -360,6 +361,23 @@ class RearPortTemplate(ComponentTemplateModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@extras_features('webhooks')
|
||||||
|
class ModuleBayTemplate(ComponentTemplateModel):
|
||||||
|
"""
|
||||||
|
A template for a ModuleBay to be created for a new parent Device.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
ordering = ('device_type', '_name')
|
||||||
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
|
def instantiate(self, device):
|
||||||
|
return ModuleBay(
|
||||||
|
device=device,
|
||||||
|
name=self.name,
|
||||||
|
label=self.label
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@extras_features('webhooks')
|
@extras_features('webhooks')
|
||||||
class DeviceBayTemplate(ComponentTemplateModel):
|
class DeviceBayTemplate(ComponentTemplateModel):
|
||||||
"""
|
"""
|
||||||
|
@ -30,6 +30,7 @@ __all__ = (
|
|||||||
'FrontPort',
|
'FrontPort',
|
||||||
'Interface',
|
'Interface',
|
||||||
'InventoryItem',
|
'InventoryItem',
|
||||||
|
'ModuleBay',
|
||||||
'PathEndpoint',
|
'PathEndpoint',
|
||||||
'PowerOutlet',
|
'PowerOutlet',
|
||||||
'PowerPort',
|
'PowerPort',
|
||||||
@ -229,7 +230,7 @@ class PathEndpoint(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Console ports
|
# Console components
|
||||||
#
|
#
|
||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
@ -260,10 +261,6 @@ class ConsolePort(ComponentModel, LinkTermination, PathEndpoint):
|
|||||||
return reverse('dcim:consoleport', kwargs={'pk': self.pk})
|
return reverse('dcim:consoleport', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Console server ports
|
|
||||||
#
|
|
||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
class ConsoleServerPort(ComponentModel, LinkTermination, PathEndpoint):
|
class ConsoleServerPort(ComponentModel, LinkTermination, PathEndpoint):
|
||||||
"""
|
"""
|
||||||
@ -293,7 +290,7 @@ class ConsoleServerPort(ComponentModel, LinkTermination, PathEndpoint):
|
|||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Power ports
|
# Power components
|
||||||
#
|
#
|
||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
@ -389,10 +386,6 @@ class PowerPort(ComponentModel, LinkTermination, PathEndpoint):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Power outlets
|
|
||||||
#
|
|
||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
class PowerOutlet(ComponentModel, LinkTermination, PathEndpoint):
|
class PowerOutlet(ComponentModel, LinkTermination, PathEndpoint):
|
||||||
"""
|
"""
|
||||||
@ -866,9 +859,24 @@ class RearPort(ComponentModel, LinkTermination):
|
|||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device bays
|
# Bays
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
|
class ModuleBay(ComponentModel):
|
||||||
|
"""
|
||||||
|
An empty space within a Device which can house a child device
|
||||||
|
"""
|
||||||
|
clone_fields = ['device']
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('device', '_name')
|
||||||
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('dcim:modulebay', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
class DeviceBay(ComponentModel):
|
class DeviceBay(ComponentModel):
|
||||||
"""
|
"""
|
||||||
|
@ -786,6 +786,9 @@ class Device(PrimaryModel, ConfigContextModel):
|
|||||||
FrontPort.objects.bulk_create(
|
FrontPort.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.frontporttemplates.all()]
|
[x.instantiate(self) for x in self.device_type.frontporttemplates.all()]
|
||||||
)
|
)
|
||||||
|
ModuleBay.objects.bulk_create(
|
||||||
|
[x.instantiate(self) for x in self.device_type.modulebaytemplates.all()]
|
||||||
|
)
|
||||||
DeviceBay.objects.bulk_create(
|
DeviceBay.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.devicebaytemplates.all()]
|
[x.instantiate(self) for x in self.device_type.devicebaytemplates.all()]
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,8 @@ import django_tables2 as tables
|
|||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
|
||||||
from dcim.models import (
|
from dcim.models import (
|
||||||
ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceRole, FrontPort, Interface, InventoryItem, Platform,
|
ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceRole, FrontPort, Interface, InventoryItem, ModuleBay,
|
||||||
PowerOutlet, PowerPort, RearPort, VirtualChassis,
|
Platform, PowerOutlet, PowerPort, RearPort, VirtualChassis,
|
||||||
)
|
)
|
||||||
from tenancy.tables import TenantColumn
|
from tenancy.tables import TenantColumn
|
||||||
from utilities.tables import (
|
from utilities.tables import (
|
||||||
@ -25,6 +25,7 @@ __all__ = (
|
|||||||
'DeviceImportTable',
|
'DeviceImportTable',
|
||||||
'DeviceInterfaceTable',
|
'DeviceInterfaceTable',
|
||||||
'DeviceInventoryItemTable',
|
'DeviceInventoryItemTable',
|
||||||
|
'DeviceModuleBayTable',
|
||||||
'DevicePowerPortTable',
|
'DevicePowerPortTable',
|
||||||
'DevicePowerOutletTable',
|
'DevicePowerOutletTable',
|
||||||
'DeviceRearPortTable',
|
'DeviceRearPortTable',
|
||||||
@ -33,6 +34,7 @@ __all__ = (
|
|||||||
'FrontPortTable',
|
'FrontPortTable',
|
||||||
'InterfaceTable',
|
'InterfaceTable',
|
||||||
'InventoryItemTable',
|
'InventoryItemTable',
|
||||||
|
'ModuleBayTable',
|
||||||
'PlatformTable',
|
'PlatformTable',
|
||||||
'PowerOutletTable',
|
'PowerOutletTable',
|
||||||
'PowerPortTable',
|
'PowerPortTable',
|
||||||
@ -716,6 +718,35 @@ class DeviceDeviceBayTable(DeviceBayTable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTable(DeviceComponentTable):
|
||||||
|
device = tables.Column(
|
||||||
|
linkify={
|
||||||
|
'viewname': 'dcim:device_modulebays',
|
||||||
|
'args': [Accessor('device_id')],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
tags = TagColumn(
|
||||||
|
url_name='dcim:modulebay_list'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(DeviceComponentTable.Meta):
|
||||||
|
model = ModuleBay
|
||||||
|
fields = ('pk', 'id', 'name', 'device', 'label', 'description', 'tags')
|
||||||
|
default_columns = ('pk', 'name', 'device', 'label', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceModuleBayTable(ModuleBayTable):
|
||||||
|
actions = ButtonsColumn(
|
||||||
|
model=ModuleBay,
|
||||||
|
buttons=('edit', 'delete')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(DeviceComponentTable.Meta):
|
||||||
|
model = ModuleBay
|
||||||
|
fields = ('pk', 'id', 'name', 'label', 'description', 'tags', 'actions')
|
||||||
|
default_columns = ('pk', 'name', 'label', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemTable(DeviceComponentTable):
|
class InventoryItemTable(DeviceComponentTable):
|
||||||
device = tables.Column(
|
device = tables.Column(
|
||||||
linkify={
|
linkify={
|
||||||
|
@ -2,7 +2,7 @@ import django_tables2 as tables
|
|||||||
|
|
||||||
from dcim.models import (
|
from dcim.models import (
|
||||||
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, FrontPortTemplate, InterfaceTemplate,
|
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, FrontPortTemplate, InterfaceTemplate,
|
||||||
Manufacturer, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
|
Manufacturer, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
|
||||||
)
|
)
|
||||||
from utilities.tables import (
|
from utilities.tables import (
|
||||||
BaseTable, BooleanColumn, ButtonsColumn, ColorColumn, LinkedCountColumn, MarkdownColumn, TagColumn, ToggleColumn,
|
BaseTable, BooleanColumn, ButtonsColumn, ColorColumn, LinkedCountColumn, MarkdownColumn, TagColumn, ToggleColumn,
|
||||||
@ -16,6 +16,7 @@ __all__ = (
|
|||||||
'FrontPortTemplateTable',
|
'FrontPortTemplateTable',
|
||||||
'InterfaceTemplateTable',
|
'InterfaceTemplateTable',
|
||||||
'ManufacturerTable',
|
'ManufacturerTable',
|
||||||
|
'ModuleBayTemplateTable',
|
||||||
'PowerOutletTemplateTable',
|
'PowerOutletTemplateTable',
|
||||||
'PowerPortTemplateTable',
|
'PowerPortTemplateTable',
|
||||||
'RearPortTemplateTable',
|
'RearPortTemplateTable',
|
||||||
@ -207,6 +208,19 @@ class RearPortTemplateTable(ComponentTemplateTable):
|
|||||||
empty_text = "None"
|
empty_text = "None"
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateTable(ComponentTemplateTable):
|
||||||
|
actions = ButtonsColumn(
|
||||||
|
model=ModuleBayTemplate,
|
||||||
|
buttons=('edit', 'delete'),
|
||||||
|
return_url_extra='%23tab_modulebays'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(ComponentTemplateTable.Meta):
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
fields = ('pk', 'name', 'label', 'description', 'actions')
|
||||||
|
empty_text = "None"
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateTable(ComponentTemplateTable):
|
class DeviceBayTemplateTable(ComponentTemplateTable):
|
||||||
actions = ButtonsColumn(
|
actions = ButtonsColumn(
|
||||||
model=DeviceBayTemplate,
|
model=DeviceBayTemplate,
|
||||||
|
@ -778,6 +778,46 @@ class RearPortTemplateTest(APIViewTestCases.APIViewTestCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
brief_fields = ['display', 'id', 'name', 'url']
|
||||||
|
bulk_update_data = {
|
||||||
|
'description': 'New description',
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||||
|
devicetype = DeviceType.objects.create(
|
||||||
|
manufacturer=manufacturer,
|
||||||
|
model='Device Type 1',
|
||||||
|
slug='device-type-1',
|
||||||
|
subdevice_role=SubdeviceRoleChoices.ROLE_PARENT
|
||||||
|
)
|
||||||
|
|
||||||
|
module_bay_templates = (
|
||||||
|
ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 1'),
|
||||||
|
ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 2'),
|
||||||
|
ModuleBayTemplate(device_type=devicetype, name='Module Bay Template 3'),
|
||||||
|
)
|
||||||
|
ModuleBayTemplate.objects.bulk_create(module_bay_templates)
|
||||||
|
|
||||||
|
cls.create_data = [
|
||||||
|
{
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name': 'Module Bay Template 4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name': 'Module Bay Template 5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'device_type': devicetype.pk,
|
||||||
|
'name': 'Module Bay Template 6',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase):
|
class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = DeviceBayTemplate
|
model = DeviceBayTemplate
|
||||||
brief_fields = ['display', 'id', 'name', 'url']
|
brief_fields = ['display', 'id', 'name', 'url']
|
||||||
@ -1369,6 +1409,45 @@ class RearPortTest(APIViewTestCases.APIViewTestCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTest(APIViewTestCases.APIViewTestCase):
|
||||||
|
model = ModuleBay
|
||||||
|
brief_fields = ['display', 'id', 'name', 'url']
|
||||||
|
bulk_update_data = {
|
||||||
|
'description': 'New description',
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||||
|
site = Site.objects.create(name='Site 1', slug='site-1')
|
||||||
|
devicerole = DeviceRole.objects.create(name='Test Device Role 1', slug='test-device-role-1', color='ff0000')
|
||||||
|
|
||||||
|
device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1')
|
||||||
|
device = Device.objects.create(device_type=device_type, device_role=devicerole, name='Device 1', site=site)
|
||||||
|
|
||||||
|
device_bays = (
|
||||||
|
ModuleBay(device=device, name='Device Bay 1'),
|
||||||
|
ModuleBay(device=device, name='Device Bay 2'),
|
||||||
|
ModuleBay(device=device, name='Device Bay 3'),
|
||||||
|
)
|
||||||
|
ModuleBay.objects.bulk_create(device_bays)
|
||||||
|
|
||||||
|
cls.create_data = [
|
||||||
|
{
|
||||||
|
'device': device.pk,
|
||||||
|
'name': 'Device Bay 4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'device': device.pk,
|
||||||
|
'name': 'Device Bay 5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'device': device.pk,
|
||||||
|
'name': 'Device Bay 6',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTest(APIViewTestCases.APIViewTestCase):
|
class DeviceBayTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
brief_fields = ['device', 'display', 'id', 'name', 'url']
|
brief_fields = ['device', 'display', 'id', 'name', 'url']
|
||||||
|
@ -678,6 +678,10 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
FrontPortTemplate(device_type=device_types[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]),
|
FrontPortTemplate(device_type=device_types[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]),
|
||||||
FrontPortTemplate(device_type=device_types[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]),
|
FrontPortTemplate(device_type=device_types[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]),
|
||||||
))
|
))
|
||||||
|
ModuleBayTemplate.objects.bulk_create((
|
||||||
|
ModuleBayTemplate(device_type=device_types[0], name='Module Bay 1'),
|
||||||
|
ModuleBayTemplate(device_type=device_types[1], name='Module Bay 2'),
|
||||||
|
))
|
||||||
DeviceBayTemplate.objects.bulk_create((
|
DeviceBayTemplate.objects.bulk_create((
|
||||||
DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1'),
|
DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1'),
|
||||||
DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2'),
|
DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2'),
|
||||||
@ -762,6 +766,12 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'device_bays': 'false'}
|
params = {'device_bays': 'false'}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
|
def test_module_bays(self):
|
||||||
|
params = {'module_bays': 'true'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'module_bays': 'false'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
class ConsolePortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
queryset = ConsolePortTemplate.objects.all()
|
queryset = ConsolePortTemplate.objects.all()
|
||||||
@ -1036,6 +1046,38 @@ class RearPortTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
filterset = ModuleBayTemplateFilterSet
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
|
||||||
|
device_types = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Model 1', slug='model-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Model 2', slug='model-2'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Model 3', slug='model-3'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(device_types)
|
||||||
|
|
||||||
|
ModuleBayTemplate.objects.bulk_create((
|
||||||
|
ModuleBayTemplate(device_type=device_types[0], name='Module Bay 1'),
|
||||||
|
ModuleBayTemplate(device_type=device_types[1], name='Module Bay 2'),
|
||||||
|
ModuleBayTemplate(device_type=device_types[2], name='Module Bay 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
def test_name(self):
|
||||||
|
params = {'name': ['Module Bay 1', 'Module Bay 2']}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_devicetype_id(self):
|
||||||
|
device_types = DeviceType.objects.all()[:2]
|
||||||
|
params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
class DeviceBayTemplateTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
queryset = DeviceBayTemplate.objects.all()
|
queryset = DeviceBayTemplate.objects.all()
|
||||||
filterset = DeviceBayTemplateFilterSet
|
filterset = DeviceBayTemplateFilterSet
|
||||||
@ -1280,6 +1322,10 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
FrontPort(device=devices[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]),
|
FrontPort(device=devices[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]),
|
||||||
FrontPort(device=devices[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]),
|
FrontPort(device=devices[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]),
|
||||||
))
|
))
|
||||||
|
ModuleBay.objects.bulk_create((
|
||||||
|
ModuleBay(device=devices[0], name='Module Bay 1'),
|
||||||
|
ModuleBay(device=devices[1], name='Module Bay 2'),
|
||||||
|
))
|
||||||
DeviceBay.objects.bulk_create((
|
DeviceBay.objects.bulk_create((
|
||||||
DeviceBay(device=devices[0], name='Device Bay 1'),
|
DeviceBay(device=devices[0], name='Device Bay 1'),
|
||||||
DeviceBay(device=devices[1], name='Device Bay 2'),
|
DeviceBay(device=devices[1], name='Device Bay 2'),
|
||||||
@ -1465,6 +1511,12 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'pass_through_ports': 'false'}
|
params = {'pass_through_ports': 'false'}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
|
def test_module_bays(self):
|
||||||
|
params = {'module_bays': 'true'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'module_bays': 'false'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
|
||||||
def test_device_bays(self):
|
def test_device_bays(self):
|
||||||
params = {'device_bays': 'true'}
|
params = {'device_bays': 'true'}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
@ -2508,6 +2560,109 @@ class RearPortTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
filterset = ModuleBayFilterSet
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
regions = (
|
||||||
|
Region(name='Region 1', slug='region-1'),
|
||||||
|
Region(name='Region 2', slug='region-2'),
|
||||||
|
Region(name='Region 3', slug='region-3'),
|
||||||
|
)
|
||||||
|
for region in regions:
|
||||||
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
|
sites = Site.objects.bulk_create((
|
||||||
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
|
Site(name='Site X', slug='site-x'),
|
||||||
|
))
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
|
||||||
|
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||||
|
|
||||||
|
locations = (
|
||||||
|
Location(name='Location 1', slug='location-1', site=sites[0]),
|
||||||
|
Location(name='Location 2', slug='location-2', site=sites[1]),
|
||||||
|
Location(name='Location 3', slug='location-3', site=sites[2]),
|
||||||
|
)
|
||||||
|
for location in locations:
|
||||||
|
location.save()
|
||||||
|
|
||||||
|
devices = (
|
||||||
|
Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], location=locations[0]),
|
||||||
|
Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[1], location=locations[1]),
|
||||||
|
Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[2], location=locations[2]),
|
||||||
|
)
|
||||||
|
Device.objects.bulk_create(devices)
|
||||||
|
|
||||||
|
module_bays = (
|
||||||
|
ModuleBay(device=devices[0], name='Module Bay 1', label='A', description='First'),
|
||||||
|
ModuleBay(device=devices[1], name='Module Bay 2', label='B', description='Second'),
|
||||||
|
ModuleBay(device=devices[2], name='Module Bay 3', label='C', description='Third'),
|
||||||
|
)
|
||||||
|
ModuleBay.objects.bulk_create(module_bays)
|
||||||
|
|
||||||
|
def test_name(self):
|
||||||
|
params = {'name': ['Module Bay 1', 'Module Bay 2']}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_label(self):
|
||||||
|
params = {'label': ['A', 'B']}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_description(self):
|
||||||
|
params = {'description': ['First', 'Second']}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_region(self):
|
||||||
|
regions = Region.objects.all()[:2]
|
||||||
|
params = {'region_id': [regions[0].pk, regions[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site(self):
|
||||||
|
sites = Site.objects.all()[:2]
|
||||||
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site': [sites[0].slug, sites[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_location(self):
|
||||||
|
locations = Location.objects.all()[:2]
|
||||||
|
params = {'location_id': [locations[0].pk, locations[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'location': [locations[0].slug, locations[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_device(self):
|
||||||
|
devices = Device.objects.all()[:2]
|
||||||
|
params = {'device_id': [devices[0].pk, devices[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'device': [devices[0].name, devices[1].name]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTestCase(TestCase, ChangeLoggedFilterSetTests):
|
class DeviceBayTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||||
queryset = DeviceBay.objects.all()
|
queryset = DeviceBay.objects.all()
|
||||||
filterset = DeviceBayFilterSet
|
filterset = DeviceBayFilterSet
|
||||||
|
@ -308,6 +308,11 @@ class DeviceTestCase(TestCase):
|
|||||||
rear_port_position=2
|
rear_port_position=2
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
|
ModuleBayTemplate(
|
||||||
|
device_type=self.device_type,
|
||||||
|
name='Module Bay 1'
|
||||||
|
).save()
|
||||||
|
|
||||||
DeviceBayTemplate(
|
DeviceBayTemplate(
|
||||||
device_type=self.device_type,
|
device_type=self.device_type,
|
||||||
name='Device Bay 1'
|
name='Device Bay 1'
|
||||||
@ -371,6 +376,11 @@ class DeviceTestCase(TestCase):
|
|||||||
rear_port_position=2
|
rear_port_position=2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ModuleBay.objects.get(
|
||||||
|
device=d,
|
||||||
|
name='Module Bay 1'
|
||||||
|
)
|
||||||
|
|
||||||
DeviceBay.objects.get(
|
DeviceBay.objects.get(
|
||||||
device=d,
|
device=d,
|
||||||
name='Device Bay 1'
|
name='Device Bay 1'
|
||||||
|
@ -554,6 +554,19 @@ class DeviceTypeTestCase(
|
|||||||
url = reverse('dcim:devicetype_frontports', kwargs={'pk': devicetype.pk})
|
url = reverse('dcim:devicetype_frontports', kwargs={'pk': devicetype.pk})
|
||||||
self.assertHttpStatus(self.client.get(url), 200)
|
self.assertHttpStatus(self.client.get(url), 200)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_devicetype_modulebays(self):
|
||||||
|
devicetype = DeviceType.objects.first()
|
||||||
|
module_bays = (
|
||||||
|
ModuleBayTemplate(device_type=devicetype, name='Module Bay 1'),
|
||||||
|
ModuleBayTemplate(device_type=devicetype, name='Module Bay 2'),
|
||||||
|
ModuleBayTemplate(device_type=devicetype, name='Module Bay 3'),
|
||||||
|
)
|
||||||
|
ModuleBayTemplate.objects.bulk_create(module_bays)
|
||||||
|
|
||||||
|
url = reverse('dcim:devicetype_modulebays', kwargs={'pk': devicetype.pk})
|
||||||
|
self.assertHttpStatus(self.client.get(url), 200)
|
||||||
|
|
||||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_devicetype_devicebays(self):
|
def test_devicetype_devicebays(self):
|
||||||
devicetype = DeviceType.objects.first()
|
devicetype = DeviceType.objects.first()
|
||||||
@ -638,6 +651,10 @@ front-ports:
|
|||||||
- name: Front Port 3
|
- name: Front Port 3
|
||||||
type: 8p8c
|
type: 8p8c
|
||||||
rear_port: Rear Port 3
|
rear_port: Rear Port 3
|
||||||
|
module-bays:
|
||||||
|
- name: Module Bay 1
|
||||||
|
- name: Module Bay 2
|
||||||
|
- name: Module Bay 3
|
||||||
device-bays:
|
device-bays:
|
||||||
- name: Device Bay 1
|
- name: Device Bay 1
|
||||||
- name: Device Bay 2
|
- name: Device Bay 2
|
||||||
@ -658,6 +675,7 @@ device-bays:
|
|||||||
'dcim.add_interfacetemplate',
|
'dcim.add_interfacetemplate',
|
||||||
'dcim.add_frontporttemplate',
|
'dcim.add_frontporttemplate',
|
||||||
'dcim.add_rearporttemplate',
|
'dcim.add_rearporttemplate',
|
||||||
|
'dcim.add_modulebaytemplate',
|
||||||
'dcim.add_devicebaytemplate',
|
'dcim.add_devicebaytemplate',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -710,6 +728,10 @@ device-bays:
|
|||||||
self.assertEqual(fp1.rear_port, rp1)
|
self.assertEqual(fp1.rear_port, rp1)
|
||||||
self.assertEqual(fp1.rear_port_position, 1)
|
self.assertEqual(fp1.rear_port_position, 1)
|
||||||
|
|
||||||
|
self.assertEqual(dt.modulebaytemplates.count(), 3)
|
||||||
|
db1 = ModuleBayTemplate.objects.first()
|
||||||
|
self.assertEqual(db1.name, 'Module Bay 1')
|
||||||
|
|
||||||
self.assertEqual(dt.devicebaytemplates.count(), 3)
|
self.assertEqual(dt.devicebaytemplates.count(), 3)
|
||||||
db1 = DeviceBayTemplate.objects.first()
|
db1 = DeviceBayTemplate.objects.first()
|
||||||
self.assertEqual(db1.name, 'Device Bay 1')
|
self.assertEqual(db1.name, 'Device Bay 1')
|
||||||
@ -1011,6 +1033,39 @@ class RearPortTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
|
model = ModuleBayTemplate
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
devicetypes = (
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'),
|
||||||
|
DeviceType(manufacturer=manufacturer, model='Device Type 2', slug='device-type-2'),
|
||||||
|
)
|
||||||
|
DeviceType.objects.bulk_create(devicetypes)
|
||||||
|
|
||||||
|
ModuleBayTemplate.objects.bulk_create((
|
||||||
|
ModuleBayTemplate(device_type=devicetypes[0], name='Module Bay Template 1'),
|
||||||
|
ModuleBayTemplate(device_type=devicetypes[0], name='Module Bay Template 2'),
|
||||||
|
ModuleBayTemplate(device_type=devicetypes[0], name='Module Bay Template 3'),
|
||||||
|
))
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name': 'Module Bay Template X',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device_type': devicetypes[1].pk,
|
||||||
|
'name_pattern': 'Module Bay Template [4-6]',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'description': 'Foo bar',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
class DeviceBayTemplateTestCase(ViewTestCases.DeviceComponentTemplateViewTestCase):
|
||||||
model = DeviceBayTemplate
|
model = DeviceBayTemplate
|
||||||
|
|
||||||
@ -1307,6 +1362,19 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
url = reverse('dcim:device_frontports', kwargs={'pk': device.pk})
|
url = reverse('dcim:device_frontports', kwargs={'pk': device.pk})
|
||||||
self.assertHttpStatus(self.client.get(url), 200)
|
self.assertHttpStatus(self.client.get(url), 200)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_device_modulebays(self):
|
||||||
|
device = Device.objects.first()
|
||||||
|
device_bays = (
|
||||||
|
ModuleBay(device=device, name='Module Bay 1'),
|
||||||
|
ModuleBay(device=device, name='Module Bay 2'),
|
||||||
|
ModuleBay(device=device, name='Module Bay 3'),
|
||||||
|
)
|
||||||
|
ModuleBay.objects.bulk_create(device_bays)
|
||||||
|
|
||||||
|
url = reverse('dcim:device_modulebays', kwargs={'pk': device.pk})
|
||||||
|
self.assertHttpStatus(self.client.get(url), 200)
|
||||||
|
|
||||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_device_devicebays(self):
|
def test_device_devicebays(self):
|
||||||
device = Device.objects.first()
|
device = Device.objects.first()
|
||||||
@ -1807,6 +1875,47 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
|||||||
self.assertHttpStatus(response, 200)
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
|
model = ModuleBay
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
device = create_test_device('Device 1')
|
||||||
|
|
||||||
|
ModuleBay.objects.bulk_create([
|
||||||
|
ModuleBay(device=device, name='Module Bay 1'),
|
||||||
|
ModuleBay(device=device, name='Module Bay 2'),
|
||||||
|
ModuleBay(device=device, name='Module Bay 3'),
|
||||||
|
])
|
||||||
|
|
||||||
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
|
|
||||||
|
cls.form_data = {
|
||||||
|
'device': device.pk,
|
||||||
|
'name': 'Module Bay X',
|
||||||
|
'description': 'A device bay',
|
||||||
|
'tags': [t.pk for t in tags],
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_create_data = {
|
||||||
|
'device': device.pk,
|
||||||
|
'name_pattern': 'Module Bay [4-6]',
|
||||||
|
'description': 'A module bay',
|
||||||
|
'tags': [t.pk for t in tags],
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'description': 'New description',
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.csv_data = (
|
||||||
|
"device,name",
|
||||||
|
"Device 1,Module Bay 4",
|
||||||
|
"Device 1,Module Bay 5",
|
||||||
|
"Device 1,Module Bay 6",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ urlpatterns = [
|
|||||||
path('device-types/<int:pk>/interfaces/', views.DeviceTypeInterfacesView.as_view(), name='devicetype_interfaces'),
|
path('device-types/<int:pk>/interfaces/', views.DeviceTypeInterfacesView.as_view(), name='devicetype_interfaces'),
|
||||||
path('device-types/<int:pk>/front-ports/', views.DeviceTypeFrontPortsView.as_view(), name='devicetype_frontports'),
|
path('device-types/<int:pk>/front-ports/', views.DeviceTypeFrontPortsView.as_view(), name='devicetype_frontports'),
|
||||||
path('device-types/<int:pk>/rear-ports/', views.DeviceTypeRearPortsView.as_view(), name='devicetype_rearports'),
|
path('device-types/<int:pk>/rear-ports/', views.DeviceTypeRearPortsView.as_view(), name='devicetype_rearports'),
|
||||||
|
path('device-types/<int:pk>/module-bays/', views.DeviceTypeModuleBaysView.as_view(), name='devicetype_modulebays'),
|
||||||
path('device-types/<int:pk>/device-bays/', views.DeviceTypeDeviceBaysView.as_view(), name='devicetype_devicebays'),
|
path('device-types/<int:pk>/device-bays/', views.DeviceTypeDeviceBaysView.as_view(), name='devicetype_devicebays'),
|
||||||
path('device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
|
path('device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
|
||||||
path('device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
|
path('device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
|
||||||
@ -183,6 +184,14 @@ urlpatterns = [
|
|||||||
path('device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
|
path('device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
|
||||||
path('device-bay-templates/<int:pk>/delete/', views.DeviceBayTemplateDeleteView.as_view(), name='devicebaytemplate_delete'),
|
path('device-bay-templates/<int:pk>/delete/', views.DeviceBayTemplateDeleteView.as_view(), name='devicebaytemplate_delete'),
|
||||||
|
|
||||||
|
# Device bay templates
|
||||||
|
path('module-bay-templates/add/', views.ModuleBayTemplateCreateView.as_view(), name='modulebaytemplate_add'),
|
||||||
|
path('module-bay-templates/edit/', views.ModuleBayTemplateBulkEditView.as_view(), name='modulebaytemplate_bulk_edit'),
|
||||||
|
path('module-bay-templates/rename/', views.ModuleBayTemplateBulkRenameView.as_view(), name='modulebaytemplate_bulk_rename'),
|
||||||
|
path('module-bay-templates/delete/', views.ModuleBayTemplateBulkDeleteView.as_view(), name='modulebaytemplate_bulk_delete'),
|
||||||
|
path('module-bay-templates/<int:pk>/edit/', views.ModuleBayTemplateEditView.as_view(), name='modulebaytemplate_edit'),
|
||||||
|
path('module-bay-templates/<int:pk>/delete/', views.ModuleBayTemplateDeleteView.as_view(), name='modulebaytemplate_delete'),
|
||||||
|
|
||||||
# Device roles
|
# Device roles
|
||||||
path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
||||||
path('device-roles/add/', views.DeviceRoleEditView.as_view(), name='devicerole_add'),
|
path('device-roles/add/', views.DeviceRoleEditView.as_view(), name='devicerole_add'),
|
||||||
@ -222,6 +231,7 @@ urlpatterns = [
|
|||||||
path('devices/<int:pk>/interfaces/', views.DeviceInterfacesView.as_view(), name='device_interfaces'),
|
path('devices/<int:pk>/interfaces/', views.DeviceInterfacesView.as_view(), name='device_interfaces'),
|
||||||
path('devices/<int:pk>/front-ports/', views.DeviceFrontPortsView.as_view(), name='device_frontports'),
|
path('devices/<int:pk>/front-ports/', views.DeviceFrontPortsView.as_view(), name='device_frontports'),
|
||||||
path('devices/<int:pk>/rear-ports/', views.DeviceRearPortsView.as_view(), name='device_rearports'),
|
path('devices/<int:pk>/rear-ports/', views.DeviceRearPortsView.as_view(), name='device_rearports'),
|
||||||
|
path('devices/<int:pk>/module-bays/', views.DeviceModuleBaysView.as_view(), name='device_modulebays'),
|
||||||
path('devices/<int:pk>/device-bays/', views.DeviceDeviceBaysView.as_view(), name='device_devicebays'),
|
path('devices/<int:pk>/device-bays/', views.DeviceDeviceBaysView.as_view(), name='device_devicebays'),
|
||||||
path('devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
|
path('devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
|
||||||
path('devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
|
path('devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
|
||||||
@ -343,6 +353,19 @@ urlpatterns = [
|
|||||||
path('rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
path('rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
|
||||||
path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
|
||||||
|
|
||||||
|
# Module bays
|
||||||
|
path('module-bays/', views.ModuleBayListView.as_view(), name='modulebay_list'),
|
||||||
|
path('module-bays/add/', views.ModuleBayCreateView.as_view(), name='modulebay_add'),
|
||||||
|
path('module-bays/import/', views.ModuleBayBulkImportView.as_view(), name='modulebay_import'),
|
||||||
|
path('module-bays/edit/', views.ModuleBayBulkEditView.as_view(), name='modulebay_bulk_edit'),
|
||||||
|
path('module-bays/rename/', views.ModuleBayBulkRenameView.as_view(), name='modulebay_bulk_rename'),
|
||||||
|
path('module-bays/delete/', views.ModuleBayBulkDeleteView.as_view(), name='modulebay_bulk_delete'),
|
||||||
|
path('module-bays/<int:pk>/', views.ModuleBayView.as_view(), name='modulebay'),
|
||||||
|
path('module-bays/<int:pk>/edit/', views.ModuleBayEditView.as_view(), name='modulebay_edit'),
|
||||||
|
path('module-bays/<int:pk>/delete/', views.ModuleBayDeleteView.as_view(), name='modulebay_delete'),
|
||||||
|
path('module-bays/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='modulebay_changelog', kwargs={'model': ModuleBay}),
|
||||||
|
path('devices/module-bays/add/', views.DeviceBulkAddModuleBayView.as_view(), name='device_bulk_add_modulebay'),
|
||||||
|
|
||||||
# Device bays
|
# Device bays
|
||||||
path('device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'),
|
path('device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'),
|
||||||
path('device-bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
path('device-bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
|
||||||
|
@ -30,9 +30,9 @@ from .constants import NONCONNECTABLE_IFACE_TYPES
|
|||||||
from .models import (
|
from .models import (
|
||||||
Cable, CablePath, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
Cable, CablePath, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||||
InventoryItem, Manufacturer, PathEndpoint, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel,
|
InventoryItem, Manufacturer, ModuleBay, ModuleBayTemplate, PathEndpoint, Platform, PowerFeed, PowerOutlet,
|
||||||
PowerPort, PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
PowerOutletTemplate, PowerPanel, PowerPort, PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort,
|
||||||
SiteGroup, VirtualChassis,
|
RearPortTemplate, Region, Site, SiteGroup, VirtualChassis,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -836,6 +836,12 @@ class DeviceTypeRearPortsView(DeviceTypeComponentsView):
|
|||||||
filterset = filtersets.RearPortTemplateFilterSet
|
filterset = filtersets.RearPortTemplateFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTypeModuleBaysView(DeviceTypeComponentsView):
|
||||||
|
child_model = ModuleBayTemplate
|
||||||
|
table = tables.ModuleBayTemplateTable
|
||||||
|
filterset = filtersets.ModuleBayTemplateFilterSet
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
|
class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
|
||||||
child_model = DeviceBayTemplate
|
child_model = DeviceBayTemplate
|
||||||
table = tables.DeviceBayTemplateTable
|
table = tables.DeviceBayTemplateTable
|
||||||
@ -861,6 +867,7 @@ class DeviceTypeImportView(generic.ObjectImportView):
|
|||||||
'dcim.add_interfacetemplate',
|
'dcim.add_interfacetemplate',
|
||||||
'dcim.add_frontporttemplate',
|
'dcim.add_frontporttemplate',
|
||||||
'dcim.add_rearporttemplate',
|
'dcim.add_rearporttemplate',
|
||||||
|
'dcim.add_modulebaytemplate',
|
||||||
'dcim.add_devicebaytemplate',
|
'dcim.add_devicebaytemplate',
|
||||||
]
|
]
|
||||||
queryset = DeviceType.objects.all()
|
queryset = DeviceType.objects.all()
|
||||||
@ -873,6 +880,7 @@ class DeviceTypeImportView(generic.ObjectImportView):
|
|||||||
('interfaces', forms.InterfaceTemplateImportForm),
|
('interfaces', forms.InterfaceTemplateImportForm),
|
||||||
('rear-ports', forms.RearPortTemplateImportForm),
|
('rear-ports', forms.RearPortTemplateImportForm),
|
||||||
('front-ports', forms.FrontPortTemplateImportForm),
|
('front-ports', forms.FrontPortTemplateImportForm),
|
||||||
|
('module-bays', forms.ModuleBayTemplateImportForm),
|
||||||
('device-bays', forms.DeviceBayTemplateImportForm),
|
('device-bays', forms.DeviceBayTemplateImportForm),
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -1132,6 +1140,40 @@ class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
table = tables.RearPortTemplateTable
|
table = tables.RearPortTemplateTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Module bay templates
|
||||||
|
#
|
||||||
|
|
||||||
|
class ModuleBayTemplateCreateView(generic.ComponentCreateView):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
form = forms.ModuleBayTemplateCreateForm
|
||||||
|
model_form = forms.ModuleBayTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateEditView(generic.ObjectEditView):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
model_form = forms.ModuleBayTemplateForm
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateDeleteView(generic.ObjectDeleteView):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateBulkEditView(generic.BulkEditView):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
table = tables.ModuleBayTemplateTable
|
||||||
|
form = forms.ModuleBayTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateBulkRenameView(generic.BulkRenameView):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView):
|
||||||
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
|
table = tables.ModuleBayTemplateTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device bay templates
|
# Device bay templates
|
||||||
#
|
#
|
||||||
@ -1388,6 +1430,13 @@ class DeviceRearPortsView(DeviceComponentsView):
|
|||||||
template_name = 'dcim/device/rearports.html'
|
template_name = 'dcim/device/rearports.html'
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceModuleBaysView(DeviceComponentsView):
|
||||||
|
child_model = ModuleBay
|
||||||
|
table = tables.DeviceModuleBayTable
|
||||||
|
filterset = filtersets.ModuleBayFilterSet
|
||||||
|
template_name = 'dcim/device/modulebays.html'
|
||||||
|
|
||||||
|
|
||||||
class DeviceDeviceBaysView(DeviceComponentsView):
|
class DeviceDeviceBaysView(DeviceComponentsView):
|
||||||
child_model = DeviceBay
|
child_model = DeviceBay
|
||||||
table = tables.DeviceDeviceBayTable
|
table = tables.DeviceDeviceBayTable
|
||||||
@ -1978,6 +2027,61 @@ class RearPortBulkDeleteView(generic.BulkDeleteView):
|
|||||||
table = tables.RearPortTable
|
table = tables.RearPortTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Module bays
|
||||||
|
#
|
||||||
|
|
||||||
|
class ModuleBayListView(generic.ObjectListView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
filterset = filtersets.ModuleBayFilterSet
|
||||||
|
filterset_form = forms.ModuleBayFilterForm
|
||||||
|
table = tables.ModuleBayTable
|
||||||
|
action_buttons = ('import', 'export')
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayView(generic.ObjectView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayCreateView(generic.ComponentCreateView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
form = forms.ModuleBayCreateForm
|
||||||
|
model_form = forms.ModuleBayForm
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayEditView(generic.ObjectEditView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
model_form = forms.ModuleBayForm
|
||||||
|
template_name = 'dcim/device_component_edit.html'
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayDeleteView(generic.ObjectDeleteView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayBulkImportView(generic.BulkImportView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
model_form = forms.ModuleBayCSVForm
|
||||||
|
table = tables.ModuleBayTable
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayBulkEditView(generic.BulkEditView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
filterset = filtersets.ModuleBayFilterSet
|
||||||
|
table = tables.ModuleBayTable
|
||||||
|
form = forms.ModuleBayBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayBulkRenameView(generic.BulkRenameView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBayBulkDeleteView(generic.BulkDeleteView):
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
filterset = filtersets.ModuleBayFilterSet
|
||||||
|
table = tables.ModuleBayTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device bays
|
# Device bays
|
||||||
#
|
#
|
||||||
@ -2234,6 +2338,17 @@ class DeviceBulkAddRearPortView(generic.BulkComponentCreateView):
|
|||||||
default_return_url = 'dcim:device_list'
|
default_return_url = 'dcim:device_list'
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView):
|
||||||
|
parent_model = Device
|
||||||
|
parent_field = 'device'
|
||||||
|
form = forms.ModuleBayBulkCreateForm
|
||||||
|
queryset = ModuleBay.objects.all()
|
||||||
|
model_form = forms.ModuleBayForm
|
||||||
|
filterset = filtersets.DeviceFilterSet
|
||||||
|
table = tables.DeviceTable
|
||||||
|
default_return_url = 'dcim:device_list'
|
||||||
|
|
||||||
|
|
||||||
class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
|
class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView):
|
||||||
parent_model = Device
|
parent_model = Device
|
||||||
parent_field = 'device'
|
parent_field = 'device'
|
||||||
|
@ -161,6 +161,7 @@ DEVICES_MENU = Menu(
|
|||||||
get_model_item('dcim', 'consoleserverport', 'Console Server Ports', actions=['import']),
|
get_model_item('dcim', 'consoleserverport', 'Console Server Ports', actions=['import']),
|
||||||
get_model_item('dcim', 'powerport', 'Power Ports', actions=['import']),
|
get_model_item('dcim', 'powerport', 'Power Ports', actions=['import']),
|
||||||
get_model_item('dcim', 'poweroutlet', 'Power Outlets', actions=['import']),
|
get_model_item('dcim', 'poweroutlet', 'Power Outlets', actions=['import']),
|
||||||
|
get_model_item('dcim', 'modulebay', 'Module Bays', actions=['import']),
|
||||||
get_model_item('dcim', 'devicebay', 'Device Bays', actions=['import']),
|
get_model_item('dcim', 'devicebay', 'Device Bays', actions=['import']),
|
||||||
get_model_item('dcim', 'inventoryitem', 'Inventory Items', actions=['import']),
|
get_model_item('dcim', 'inventoryitem', 'Inventory Items', actions=['import']),
|
||||||
),
|
),
|
||||||
|
@ -69,6 +69,13 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_devicebay %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{% url 'dcim:modulebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_modulebays' pk=object.pk %}">
|
||||||
|
Module Bays
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% if perms.dcim.add_devicebay %}
|
{% if perms.dcim.add_devicebay %}
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}">
|
<a class="dropdown-item" href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}">
|
||||||
@ -151,6 +158,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with modulebay_count=object.modulebays.count %}
|
||||||
|
{% if modulebay_count %}
|
||||||
|
<li role="presentation" class="nav-item">
|
||||||
|
<a class="nav-link {% if active_tab == 'module-bays' %} active{% endif %}" href="{% url 'dcim:device_modulebays' pk=object.pk %}">Module Bays {% badge modulebay_count %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
{% with devicebay_count=object.devicebays.count %}
|
{% with devicebay_count=object.devicebays.count %}
|
||||||
{% if devicebay_count %}
|
{% if devicebay_count %}
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
|
43
netbox/templates/dcim/device/modulebays.html
Normal file
43
netbox/templates/dcim/device/modulebays.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{% extends 'dcim/device/base.html' %}
|
||||||
|
{% load render_table from django_tables2 %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% include 'inc/table_controls_htmx.html' with table_modal="DeviceModuleBayTable_config" %}
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body" id="object_list">
|
||||||
|
{% include 'htmx/table.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="noprint bulk-buttons">
|
||||||
|
<div class="bulk-button-group">
|
||||||
|
{% if perms.dcim.change_modulebay %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:modulebay_bulk_rename' %}?return_url={{ object.get_absolute_url }}%23tab_modulebays" class="btn btn-outline-warning btn-sm">
|
||||||
|
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:modulebay_bulk_edit' %}?device={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_modulebays" class="btn btn-warning btn-sm">
|
||||||
|
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.delete_modulebay %}
|
||||||
|
<button type="submit" formaction="{% url 'dcim:modulebay_bulk_delete' %}?return_url={{ object.get_absolute_url }}%23tab_modulebays" class="btn btn-outline-danger btn-sm">
|
||||||
|
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete selected
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if perms.dcim.add_modulebay %}
|
||||||
|
<div class="bulk-button-group">
|
||||||
|
<a href="{% url 'dcim:modulebay_add' %}?device={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_modulebays" class="btn btn-primary btn-sm">
|
||||||
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Module Bays
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% table_config_form table %}
|
||||||
|
{% endblock %}
|
@ -56,6 +56,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_modulebay %}
|
||||||
|
<li>
|
||||||
|
<button type="submit" formaction="{% url 'dcim:device_bulk_add_modulebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||||
|
Module Bays
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% if perms.dcim.add_inventoryitem %}
|
{% if perms.dcim.add_inventoryitem %}
|
||||||
<li>
|
<li>
|
||||||
<button type="submit" formaction="{% url 'dcim:device_bulk_add_inventoryitem' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
<button type="submit" formaction="{% url 'dcim:device_bulk_add_inventoryitem' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="dropdown-item">
|
||||||
|
@ -38,6 +38,9 @@
|
|||||||
{% if perms.dcim.add_rearporttemplate %}
|
{% if perms.dcim.add_rearporttemplate %}
|
||||||
<li><a class="dropdown-item" href="{% url 'dcim:rearporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_rearports">Rear Ports</a></li>
|
<li><a class="dropdown-item" href="{% url 'dcim:rearporttemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_rearports">Rear Ports</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_modulebaytemplate %}
|
||||||
|
<li><a class="dropdown-item" href="{% url 'dcim:modulebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_modulebays">Module Bays</a></li>
|
||||||
|
{% endif %}
|
||||||
{% if perms.dcim.add_devicebaytemplate %}
|
{% if perms.dcim.add_devicebaytemplate %}
|
||||||
<li><a class="dropdown-item" href="{% url 'dcim:devicebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_devicebays">Device Bays</a></li>
|
<li><a class="dropdown-item" href="{% url 'dcim:devicebaytemplate_add' %}?device_type={{ object.pk }}&return_url={{ object.get_absolute_url }}%23tab_devicebays">Device Bays</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -109,6 +112,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with modulebay_count=object.modulebaytemplates.count %}
|
||||||
|
{% if modulebay_count %}
|
||||||
|
<li role="presentation" class="nav-item">
|
||||||
|
<a class="nav-link {% if active_tab == 'module-bay-templates' %} active{% endif %}" href="{% url 'dcim:devicetype_modulebays' pk=object.pk %}">Module Bays {% badge modulebay_count %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
{% with devicebay_count=object.devicebaytemplates.count %}
|
{% with devicebay_count=object.devicebaytemplates.count %}
|
||||||
{% if devicebay_count %}
|
{% if devicebay_count %}
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
|
69
netbox/templates/dcim/modulebay.html
Normal file
69
netbox/templates/dcim/modulebay.html
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{% extends 'dcim/device_component.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load plugins %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">Module Bay</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-hover attr-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Device</th>
|
||||||
|
<td>
|
||||||
|
<a href="{{ object.device.get_absolute_url }}">{{ object.device }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Name</th>
|
||||||
|
<td>{{ object.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Label</th>
|
||||||
|
<td>{{ object.label|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Description</th>
|
||||||
|
<td>{{ object.description|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'inc/panels/custom_fields.html' %}
|
||||||
|
{% include 'inc/panels/tags.html' %}
|
||||||
|
{% plugin_left_page object %}
|
||||||
|
</div>
|
||||||
|
<div class="col col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">Installed Module</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if object.module %}
|
||||||
|
{% with module=object.module %}
|
||||||
|
<table class="table table-hover attr-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Module</th>
|
||||||
|
<td>
|
||||||
|
<a href="{{ module.get_absolute_url }}">{{ module }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Module Type</th>
|
||||||
|
<td>{{ module.module_type }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<div class="text-muted">None</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% plugin_right_page object %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col col-md-12">
|
||||||
|
{% plugin_full_width_page object %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user