mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-18 04:56:29 -06:00
Merge branch 'develop-2.9' into 2006-scripts-reports-background
This commit is contained in:
commit
4a74927fa2
@ -19,6 +19,7 @@ NetBox v2.9 replaces Django's built-in permissions framework with one that suppo
|
|||||||
* [#4793](https://github.com/netbox-community/netbox/issues/4793) - Add `description` field to device component templates
|
* [#4793](https://github.com/netbox-community/netbox/issues/4793) - Add `description` field to device component templates
|
||||||
* [#4795](https://github.com/netbox-community/netbox/issues/4795) - Add bulk disconnect capability for console and power ports
|
* [#4795](https://github.com/netbox-community/netbox/issues/4795) - Add bulk disconnect capability for console and power ports
|
||||||
* [#4807](https://github.com/netbox-community/netbox/issues/4807) - Add bulk edit ability for device bay templates
|
* [#4807](https://github.com/netbox-community/netbox/issues/4807) - Add bulk edit ability for device bay templates
|
||||||
|
* [#4817](https://github.com/netbox-community/netbox/issues/4817) - Standardize device/VM component `name` field to 64 characters
|
||||||
|
|
||||||
### Configuration Changes
|
### Configuration Changes
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.test import override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from circuits.choices import *
|
from circuits.choices import *
|
||||||
@ -45,6 +46,7 @@ class ProviderTest(APIViewTestCases.APIViewTestCase):
|
|||||||
)
|
)
|
||||||
Provider.objects.bulk_create(providers)
|
Provider.objects.bulk_create(providers)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_get_provider_graphs(self):
|
def test_get_provider_graphs(self):
|
||||||
"""
|
"""
|
||||||
Test retrieval of Graphs assigned to Providers.
|
Test retrieval of Graphs assigned to Providers.
|
||||||
|
@ -311,7 +311,7 @@ class RearPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPortTemplate
|
model = RearPortTemplate
|
||||||
fields = ['id', 'device_type', 'name', 'type', 'positions', 'description']
|
fields = ['id', 'device_type', 'name', 'label', 'type', 'positions', 'description']
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
@ -321,7 +321,7 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
fields = ['id', 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
|
fields = ['id', 'device_type', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description']
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||||
@ -559,7 +559,7 @@ class RearPortSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPort
|
model = RearPort
|
||||||
fields = ['id', 'device', 'name', 'type', 'positions', 'description', 'cable', 'tags']
|
fields = ['id', 'device', 'name', 'label', 'type', 'positions', 'description', 'cable', 'tags']
|
||||||
|
|
||||||
|
|
||||||
class FrontPortRearPortSerializer(WritableNestedSerializer):
|
class FrontPortRearPortSerializer(WritableNestedSerializer):
|
||||||
@ -570,7 +570,7 @@ class FrontPortRearPortSerializer(WritableNestedSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RearPort
|
model = RearPort
|
||||||
fields = ['id', 'url', 'name']
|
fields = ['id', 'url', 'name', 'label']
|
||||||
|
|
||||||
|
|
||||||
class FrontPortSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
class FrontPortSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
||||||
@ -581,7 +581,9 @@ class FrontPortSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
fields = ['id', 'device', 'name', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'tags']
|
fields = [
|
||||||
|
'id', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'tags',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceBaySerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
class DeviceBaySerializer(TaggedObjectSerializer, ValidatedModelSerializer):
|
||||||
|
@ -384,28 +384,28 @@ class DeviceTypeFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFil
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _console_ports(self, queryset, name, value):
|
def _console_ports(self, queryset, name, value):
|
||||||
return queryset.exclude(consoleport_templates__isnull=value)
|
return queryset.exclude(consoleporttemplates__isnull=value)
|
||||||
|
|
||||||
def _console_server_ports(self, queryset, name, value):
|
def _console_server_ports(self, queryset, name, value):
|
||||||
return queryset.exclude(consoleserverport_templates__isnull=value)
|
return queryset.exclude(consoleserverporttemplates__isnull=value)
|
||||||
|
|
||||||
def _power_ports(self, queryset, name, value):
|
def _power_ports(self, queryset, name, value):
|
||||||
return queryset.exclude(powerport_templates__isnull=value)
|
return queryset.exclude(powerporttemplates__isnull=value)
|
||||||
|
|
||||||
def _power_outlets(self, queryset, name, value):
|
def _power_outlets(self, queryset, name, value):
|
||||||
return queryset.exclude(poweroutlet_templates__isnull=value)
|
return queryset.exclude(poweroutlettemplates__isnull=value)
|
||||||
|
|
||||||
def _interfaces(self, queryset, name, value):
|
def _interfaces(self, queryset, name, value):
|
||||||
return queryset.exclude(interface_templates__isnull=value)
|
return queryset.exclude(interfacetemplates__isnull=value)
|
||||||
|
|
||||||
def _pass_through_ports(self, queryset, name, value):
|
def _pass_through_ports(self, queryset, name, value):
|
||||||
return queryset.exclude(
|
return queryset.exclude(
|
||||||
frontport_templates__isnull=value,
|
frontporttemplates__isnull=value,
|
||||||
rearport_templates__isnull=value
|
rearporttemplates__isnull=value
|
||||||
)
|
)
|
||||||
|
|
||||||
def _device_bays(self, queryset, name, value):
|
def _device_bays(self, queryset, name, value):
|
||||||
return queryset.exclude(device_bay_templates__isnull=value)
|
return queryset.exclude(devicebaytemplates__isnull=value)
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
|
class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
|
||||||
@ -656,7 +656,7 @@ class DeviceFilterSet(
|
|||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
Q(name__icontains=value) |
|
Q(name__icontains=value) |
|
||||||
Q(serial__icontains=value.strip()) |
|
Q(serial__icontains=value.strip()) |
|
||||||
Q(inventory_items__serial__icontains=value.strip()) |
|
Q(inventoryitems__serial__icontains=value.strip()) |
|
||||||
Q(asset_tag__icontains=value.strip()) |
|
Q(asset_tag__icontains=value.strip()) |
|
||||||
Q(comments__icontains=value)
|
Q(comments__icontains=value)
|
||||||
).distinct()
|
).distinct()
|
||||||
@ -698,7 +698,7 @@ class DeviceFilterSet(
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _device_bays(self, queryset, name, value):
|
def _device_bays(self, queryset, name, value):
|
||||||
return queryset.exclude(device_bays__isnull=value)
|
return queryset.exclude(devicebays__isnull=value)
|
||||||
|
|
||||||
|
|
||||||
class DeviceComponentFilterSet(django_filters.FilterSet):
|
class DeviceComponentFilterSet(django_filters.FilterSet):
|
||||||
@ -747,6 +747,7 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
Q(name__icontains=value) |
|
Q(name__icontains=value) |
|
||||||
|
Q(label__icontains=value) |
|
||||||
Q(description__icontains=value)
|
Q(description__icontains=value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,7 +59,6 @@ def get_device_by_name_or_pk(name):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceComponentFilterForm(BootstrapMixin, forms.Form):
|
class DeviceComponentFilterForm(BootstrapMixin, forms.Form):
|
||||||
|
|
||||||
field_order = [
|
field_order = [
|
||||||
'q', 'region', 'site'
|
'q', 'region', 'site'
|
||||||
]
|
]
|
||||||
@ -127,7 +126,11 @@ class InterfaceCommonForm:
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class LabeledComponentForm(BootstrapMixin, forms.Form):
|
class ComponentForm(BootstrapMixin, forms.Form):
|
||||||
|
"""
|
||||||
|
Subclass this form when facilitating the creation of one or more device component or component templates based on
|
||||||
|
a name pattern.
|
||||||
|
"""
|
||||||
name_pattern = ExpandableNameField(
|
name_pattern = ExpandableNameField(
|
||||||
label='Name'
|
label='Name'
|
||||||
)
|
)
|
||||||
@ -1033,7 +1036,7 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
# Device component templates
|
# Device component templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class ComponentTemplateCreateForm(LabeledComponentForm):
|
class ComponentTemplateCreateForm(ComponentForm):
|
||||||
"""
|
"""
|
||||||
Base form for the creation of device component templates.
|
Base form for the creation of device component templates.
|
||||||
"""
|
"""
|
||||||
@ -1350,7 +1353,7 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'device_type', 'name', 'type', 'rear_port', 'rear_port_position', 'description',
|
'device_type', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'device_type': forms.HiddenInput(),
|
'device_type': forms.HiddenInput(),
|
||||||
@ -1389,7 +1392,7 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm):
|
|||||||
# Determine which rear port positions are occupied. These will be excluded from the list of available mappings.
|
# Determine which rear port positions are occupied. These will be excluded from the list of available mappings.
|
||||||
occupied_port_positions = [
|
occupied_port_positions = [
|
||||||
(front_port.rear_port_id, front_port.rear_port_position)
|
(front_port.rear_port_id, front_port.rear_port_position)
|
||||||
for front_port in device_type.frontport_templates.all()
|
for front_port in device_type.frontporttemplates.all()
|
||||||
]
|
]
|
||||||
|
|
||||||
# Populate rear port choices
|
# Populate rear port choices
|
||||||
@ -1430,6 +1433,10 @@ class FrontPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
queryset=FrontPortTemplate.objects.all(),
|
queryset=FrontPortTemplate.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
label = forms.CharField(
|
||||||
|
max_length=64,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
type = forms.ChoiceField(
|
type = forms.ChoiceField(
|
||||||
choices=add_blank_choice(PortTypeChoices),
|
choices=add_blank_choice(PortTypeChoices),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1448,7 +1455,7 @@ class RearPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = RearPortTemplate
|
model = RearPortTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'device_type', 'name', 'type', 'positions', 'description',
|
'device_type', 'name', 'label', 'type', 'positions', 'description',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'device_type': forms.HiddenInput(),
|
'device_type': forms.HiddenInput(),
|
||||||
@ -1474,6 +1481,10 @@ class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
queryset=RearPortTemplate.objects.all(),
|
queryset=RearPortTemplate.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
label = forms.CharField(
|
||||||
|
max_length=64,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
type = forms.ChoiceField(
|
type = forms.ChoiceField(
|
||||||
choices=add_blank_choice(PortTypeChoices),
|
choices=add_blank_choice(PortTypeChoices),
|
||||||
required=False,
|
required=False,
|
||||||
@ -2248,7 +2259,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
|
|||||||
# Device components
|
# Device components
|
||||||
#
|
#
|
||||||
|
|
||||||
class ComponentCreateForm(LabeledComponentForm):
|
class ComponentCreateForm(ComponentForm):
|
||||||
"""
|
"""
|
||||||
Base form for the creation of device components.
|
Base form for the creation of device components.
|
||||||
"""
|
"""
|
||||||
@ -2261,7 +2272,7 @@ class ComponentCreateForm(LabeledComponentForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceBulkAddComponentForm(LabeledComponentForm):
|
class DeviceBulkAddComponentForm(ComponentForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -2967,13 +2978,12 @@ class InterfaceCSVForm(CSVModelForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Limit LAG choices to interfaces belonging to this device (or VC master)
|
# Limit LAG choices to interfaces belonging to this device (or VC master)
|
||||||
|
device = None
|
||||||
if self.is_bound and 'device' in self.data:
|
if self.is_bound and 'device' in self.data:
|
||||||
try:
|
try:
|
||||||
device = self.fields['device'].to_python(self.data['device'])
|
device = self.fields['device'].to_python(self.data['device'])
|
||||||
except forms.ValidationError:
|
except forms.ValidationError:
|
||||||
device = None
|
pass
|
||||||
else:
|
|
||||||
device = self.instance.device
|
|
||||||
|
|
||||||
if device:
|
if device:
|
||||||
self.fields['lag'].queryset = Interface.objects.filter(
|
self.fields['lag'].queryset = Interface.objects.filter(
|
||||||
@ -3013,7 +3023,7 @@ class FrontPortForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
fields = [
|
fields = [
|
||||||
'device', 'name', 'type', 'rear_port', 'rear_port_position', 'description', 'tags',
|
'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'device': forms.HiddenInput(),
|
'device': forms.HiddenInput(),
|
||||||
@ -3094,14 +3104,14 @@ class FrontPortCreateForm(ComponentCreateForm):
|
|||||||
|
|
||||||
|
|
||||||
# class FrontPortBulkCreateForm(
|
# class FrontPortBulkCreateForm(
|
||||||
# form_from_model(FrontPort, ['type', 'description', 'tags']),
|
# form_from_model(FrontPort, ['label', 'type', 'description', 'tags']),
|
||||||
# DeviceBulkAddComponentForm
|
# DeviceBulkAddComponentForm
|
||||||
# ):
|
# ):
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
|
|
||||||
class FrontPortBulkEditForm(
|
class FrontPortBulkEditForm(
|
||||||
form_from_model(FrontPort, ['type', 'description']),
|
form_from_model(FrontPort, ['label', 'type', 'description']),
|
||||||
BootstrapMixin,
|
BootstrapMixin,
|
||||||
AddRemoveTagsForm,
|
AddRemoveTagsForm,
|
||||||
BulkEditForm
|
BulkEditForm
|
||||||
@ -3112,9 +3122,7 @@ class FrontPortBulkEditForm(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = [
|
nullable_fields = ('label', 'description')
|
||||||
'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class FrontPortCSVForm(CSVModelForm):
|
class FrontPortCSVForm(CSVModelForm):
|
||||||
@ -3185,7 +3193,7 @@ class RearPortForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = RearPort
|
model = RearPort
|
||||||
fields = [
|
fields = [
|
||||||
'device', 'name', 'type', 'positions', 'description', 'tags',
|
'device', 'name', 'label', 'type', 'positions', 'description', 'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'device': forms.HiddenInput(),
|
'device': forms.HiddenInput(),
|
||||||
@ -3210,14 +3218,14 @@ class RearPortCreateForm(ComponentCreateForm):
|
|||||||
|
|
||||||
|
|
||||||
class RearPortBulkCreateForm(
|
class RearPortBulkCreateForm(
|
||||||
form_from_model(RearPort, ['type', 'positions', 'description', 'tags']),
|
form_from_model(RearPort, ['label', 'type', 'positions', 'description', 'tags']),
|
||||||
DeviceBulkAddComponentForm
|
DeviceBulkAddComponentForm
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RearPortBulkEditForm(
|
class RearPortBulkEditForm(
|
||||||
form_from_model(RearPort, ['type', 'description']),
|
form_from_model(RearPort, ['label', 'type', 'description']),
|
||||||
BootstrapMixin,
|
BootstrapMixin,
|
||||||
AddRemoveTagsForm,
|
AddRemoveTagsForm,
|
||||||
BulkEditForm
|
BulkEditForm
|
||||||
@ -3228,9 +3236,7 @@ class RearPortBulkEditForm(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = [
|
nullable_fields = ('label', 'description')
|
||||||
'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortCSVForm(CSVModelForm):
|
class RearPortCSVForm(CSVModelForm):
|
||||||
@ -3392,17 +3398,11 @@ class InventoryItemForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'device', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'tags',
|
'name', 'label', 'device', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'tags',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemCreateForm(BootstrapMixin, forms.Form):
|
class InventoryItemCreateForm(ComponentCreateForm):
|
||||||
device = DynamicModelChoiceField(
|
|
||||||
queryset=Device.objects.prefetch_related('device_type__manufacturer')
|
|
||||||
)
|
|
||||||
name_pattern = ExpandableNameField(
|
|
||||||
label='Name'
|
|
||||||
)
|
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -3443,7 +3443,7 @@ class InventoryItemCSVForm(CSVModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkCreateForm(
|
class InventoryItemBulkCreateForm(
|
||||||
form_from_model(InventoryItem, ['manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'tags']),
|
form_from_model(InventoryItem, ['label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'tags']),
|
||||||
DeviceBulkAddComponentForm
|
DeviceBulkAddComponentForm
|
||||||
):
|
):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
@ -3452,68 +3452,27 @@ class InventoryItemBulkCreateForm(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm):
|
class InventoryItemBulkEditForm(
|
||||||
|
form_from_model(InventoryItem, ['label', 'manufacturer', 'part_id', 'description']),
|
||||||
|
BootstrapMixin,
|
||||||
|
AddRemoveTagsForm,
|
||||||
|
BulkEditForm
|
||||||
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=InventoryItem.objects.all(),
|
queryset=InventoryItem.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
device = DynamicModelChoiceField(
|
|
||||||
queryset=Device.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
part_id = forms.CharField(
|
|
||||||
max_length=50,
|
|
||||||
required=False,
|
|
||||||
label='Part ID'
|
|
||||||
)
|
|
||||||
description = forms.CharField(
|
|
||||||
max_length=100,
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = [
|
nullable_fields = ('label', 'manufacturer', 'part_id', 'description')
|
||||||
'manufacturer', 'part_id', 'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemFilterForm(BootstrapMixin, forms.Form):
|
class InventoryItemFilterForm(DeviceComponentFilterForm):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
q = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label='Search'
|
|
||||||
)
|
|
||||||
region = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Region.objects.all(),
|
|
||||||
to_field_name='slug',
|
|
||||||
required=False,
|
|
||||||
widget=APISelectMultiple(
|
|
||||||
value_field="slug",
|
|
||||||
filter_for={
|
|
||||||
'site': 'region'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
site = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Site.objects.all(),
|
|
||||||
to_field_name='slug',
|
|
||||||
required=False,
|
|
||||||
widget=APISelectMultiple(
|
|
||||||
value_field="slug",
|
|
||||||
filter_for={
|
|
||||||
'device_id': 'site'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
device_id = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Device.objects.all(),
|
|
||||||
required=False,
|
|
||||||
label='Device'
|
|
||||||
)
|
|
||||||
manufacturer = DynamicModelMultipleChoiceField(
|
manufacturer = DynamicModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
@ -3522,6 +3481,12 @@ class InventoryItemFilterForm(BootstrapMixin, forms.Form):
|
|||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
serial = forms.CharField(
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
asset_tag = forms.CharField(
|
||||||
|
required=False
|
||||||
|
)
|
||||||
discovered = forms.NullBooleanField(
|
discovered = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2(
|
widget=StaticSelect2(
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# Generated by Django 3.0.7 on 2020-06-04 20:37
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
@ -10,16 +8,6 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
|
||||||
model_name='interface',
|
|
||||||
name='label',
|
|
||||||
field=models.CharField(blank=True, max_length=64),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='interfacetemplate',
|
|
||||||
name='label',
|
|
||||||
field=models.CharField(blank=True, max_length=64),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='consoleport',
|
model_name='consoleport',
|
||||||
name='label',
|
name='label',
|
||||||
@ -40,6 +28,41 @@ class Migration(migrations.Migration):
|
|||||||
name='label',
|
name='label',
|
||||||
field=models.CharField(blank=True, max_length=64),
|
field=models.CharField(blank=True, max_length=64),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicebay',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='devicebaytemplate',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='frontport',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interface',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interfacetemplate',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='inventoryitem',
|
||||||
|
name='label',
|
||||||
|
field=models.CharField(blank=True, max_length=64),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='poweroutlet',
|
model_name='poweroutlet',
|
||||||
name='label',
|
name='label',
|
||||||
@ -61,12 +84,12 @@ class Migration(migrations.Migration):
|
|||||||
field=models.CharField(blank=True, max_length=64),
|
field=models.CharField(blank=True, max_length=64),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='devicebay',
|
model_name='rearport',
|
||||||
name='label',
|
name='label',
|
||||||
field=models.CharField(blank=True, max_length=64),
|
field=models.CharField(blank=True, max_length=64),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='devicebaytemplate',
|
model_name='rearporttemplate',
|
||||||
name='label',
|
name='label',
|
||||||
field=models.CharField(blank=True, max_length=64),
|
field=models.CharField(blank=True, max_length=64),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# Generated by Django 3.0.6 on 2020-06-22 16:03
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@ -15,4 +14,11 @@ class Migration(migrations.Migration):
|
|||||||
model_name='interface',
|
model_name='interface',
|
||||||
name='virtual_machine',
|
name='virtual_machine',
|
||||||
),
|
),
|
||||||
|
# device is now a required field
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='interface',
|
||||||
|
name='device',
|
||||||
|
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='dcim.Device'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
120
netbox/dcim/migrations/0112_standardize_components.py
Normal file
120
netbox/dcim/migrations/0112_standardize_components.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0111_component_template_description'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
# Set max_length=64 for all name fields
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleport',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleporttemplate',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleserverport',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleserverporttemplate',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicebay',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicebaytemplate',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='inventoryitem',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='poweroutlet',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='poweroutlettemplate',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='powerport',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='powerporttemplate',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=64),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Update related_name for necessary component and component template models
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleporttemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consoleporttemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleserverporttemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='consoleserverporttemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicebay',
|
||||||
|
name='device',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='devicebays', to='dcim.Device'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicebaytemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='devicebaytemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='frontporttemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontporttemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='interfacetemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfacetemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='inventoryitem',
|
||||||
|
name='device',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inventoryitems', to='dcim.Device'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='poweroutlettemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='poweroutlettemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='powerporttemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='powerporttemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rearporttemplate',
|
||||||
|
name='device_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rearporttemplates', to='dcim.DeviceType'),
|
||||||
|
),
|
||||||
|
]
|
@ -678,7 +678,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
'device_type__manufacturer',
|
'device_type__manufacturer',
|
||||||
'device_role'
|
'device_role'
|
||||||
).annotate(
|
).annotate(
|
||||||
devicebay_count=Count('device_bays')
|
devicebay_count=Count('devicebays')
|
||||||
).exclude(
|
).exclude(
|
||||||
pk=exclude
|
pk=exclude
|
||||||
).filter(
|
).filter(
|
||||||
@ -1049,23 +1049,23 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
))
|
))
|
||||||
|
|
||||||
# Component templates
|
# Component templates
|
||||||
if self.consoleport_templates.exists():
|
if self.consoleporttemplates.exists():
|
||||||
data['console-ports'] = [
|
data['console-ports'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
'type': c.type,
|
'type': c.type,
|
||||||
}
|
}
|
||||||
for c in self.consoleport_templates.all()
|
for c in self.consoleporttemplates.all()
|
||||||
]
|
]
|
||||||
if self.consoleserverport_templates.exists():
|
if self.consoleserverporttemplates.exists():
|
||||||
data['console-server-ports'] = [
|
data['console-server-ports'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
'type': c.type,
|
'type': c.type,
|
||||||
}
|
}
|
||||||
for c in self.consoleserverport_templates.all()
|
for c in self.consoleserverporttemplates.all()
|
||||||
]
|
]
|
||||||
if self.powerport_templates.exists():
|
if self.powerporttemplates.exists():
|
||||||
data['power-ports'] = [
|
data['power-ports'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
@ -1073,9 +1073,9 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
'maximum_draw': c.maximum_draw,
|
'maximum_draw': c.maximum_draw,
|
||||||
'allocated_draw': c.allocated_draw,
|
'allocated_draw': c.allocated_draw,
|
||||||
}
|
}
|
||||||
for c in self.powerport_templates.all()
|
for c in self.powerporttemplates.all()
|
||||||
]
|
]
|
||||||
if self.poweroutlet_templates.exists():
|
if self.poweroutlettemplates.exists():
|
||||||
data['power-outlets'] = [
|
data['power-outlets'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
@ -1083,18 +1083,18 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
'power_port': c.power_port.name if c.power_port else None,
|
'power_port': c.power_port.name if c.power_port else None,
|
||||||
'feed_leg': c.feed_leg,
|
'feed_leg': c.feed_leg,
|
||||||
}
|
}
|
||||||
for c in self.poweroutlet_templates.all()
|
for c in self.poweroutlettemplates.all()
|
||||||
]
|
]
|
||||||
if self.interface_templates.exists():
|
if self.interfacetemplates.exists():
|
||||||
data['interfaces'] = [
|
data['interfaces'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
'type': c.type,
|
'type': c.type,
|
||||||
'mgmt_only': c.mgmt_only,
|
'mgmt_only': c.mgmt_only,
|
||||||
}
|
}
|
||||||
for c in self.interface_templates.all()
|
for c in self.interfacetemplates.all()
|
||||||
]
|
]
|
||||||
if self.frontport_templates.exists():
|
if self.frontporttemplates.exists():
|
||||||
data['front-ports'] = [
|
data['front-ports'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
@ -1102,23 +1102,23 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
'rear_port': c.rear_port.name,
|
'rear_port': c.rear_port.name,
|
||||||
'rear_port_position': c.rear_port_position,
|
'rear_port_position': c.rear_port_position,
|
||||||
}
|
}
|
||||||
for c in self.frontport_templates.all()
|
for c in self.frontporttemplates.all()
|
||||||
]
|
]
|
||||||
if self.rearport_templates.exists():
|
if self.rearporttemplates.exists():
|
||||||
data['rear-ports'] = [
|
data['rear-ports'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
'type': c.type,
|
'type': c.type,
|
||||||
'positions': c.positions,
|
'positions': c.positions,
|
||||||
}
|
}
|
||||||
for c in self.rearport_templates.all()
|
for c in self.rearporttemplates.all()
|
||||||
]
|
]
|
||||||
if self.device_bay_templates.exists():
|
if self.devicebaytemplates.exists():
|
||||||
data['device-bays'] = [
|
data['device-bays'] = [
|
||||||
{
|
{
|
||||||
'name': c.name,
|
'name': c.name,
|
||||||
}
|
}
|
||||||
for c in self.device_bay_templates.all()
|
for c in self.devicebaytemplates.all()
|
||||||
]
|
]
|
||||||
|
|
||||||
return yaml.dump(dict(data), sort_keys=False)
|
return yaml.dump(dict(data), sort_keys=False)
|
||||||
@ -1159,7 +1159,7 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT
|
self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT
|
||||||
) and self.device_bay_templates.count():
|
) and self.devicebaytemplates.count():
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'subdevice_role': "Must delete all device bay templates associated with this device before "
|
'subdevice_role': "Must delete all device bay templates associated with this device before "
|
||||||
"declassifying it as a parent device."
|
"declassifying it as a parent device."
|
||||||
@ -1634,28 +1634,28 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
# If this is a new Device, instantiate all of the related components per the DeviceType definition
|
# If this is a new Device, instantiate all of the related components per the DeviceType definition
|
||||||
if is_new:
|
if is_new:
|
||||||
ConsolePort.objects.bulk_create(
|
ConsolePort.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.consoleport_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.consoleporttemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
ConsoleServerPort.objects.bulk_create(
|
ConsoleServerPort.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.consoleserverport_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.consoleserverporttemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
PowerPort.objects.bulk_create(
|
PowerPort.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.powerport_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.powerporttemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
PowerOutlet.objects.bulk_create(
|
PowerOutlet.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.poweroutlet_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.poweroutlettemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
Interface.objects.bulk_create(
|
Interface.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.interface_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.interfacetemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
RearPort.objects.bulk_create(
|
RearPort.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.rearport_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.rearporttemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
FrontPort.objects.bulk_create(
|
FrontPort.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.frontport_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.frontporttemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
DeviceBay.objects.bulk_create(
|
DeviceBay.objects.bulk_create(
|
||||||
[x.instantiate(self) for x in self.device_type.device_bay_templates.unrestricted()]
|
[x.instantiate(self) for x in self.device_type.devicebaytemplates.unrestricted()]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update Site and Rack assignment for any child Devices
|
# Update Site and Rack assignment for any child Devices
|
||||||
|
@ -27,6 +27,24 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ComponentTemplateModel(models.Model):
|
class ComponentTemplateModel(models.Model):
|
||||||
|
device_type = models.ForeignKey(
|
||||||
|
to='dcim.DeviceType',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='%(class)ss'
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=64
|
||||||
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
label = models.CharField(
|
||||||
|
max_length=64,
|
||||||
|
blank=True,
|
||||||
|
help_text="Physical label"
|
||||||
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True
|
blank=True
|
||||||
@ -68,24 +86,6 @@ class ConsolePortTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
A template for a ConsolePort to be created for a new Device.
|
A template for a ConsolePort to be created for a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='consoleport_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
@ -108,24 +108,6 @@ class ConsoleServerPortTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
A template for a ConsoleServerPort to be created for a new Device.
|
A template for a ConsoleServerPort to be created for a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='consoleserverport_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
@ -148,24 +130,6 @@ class PowerPortTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
A template for a PowerPort to be created for a new Device.
|
A template for a PowerPort to be created for a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='powerport_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
@ -202,24 +166,6 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
A template for a PowerOutlet to be created for a new Device.
|
A template for a PowerOutlet to be created for a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='poweroutlet_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
@ -269,25 +215,13 @@ class InterfaceTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
A template for a physical data interface on a new Device.
|
A template for a physical data interface on a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
# Override ComponentTemplateModel._name to specify naturalize_interface function
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='interface_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=64
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
_name = NaturalOrderingField(
|
||||||
target_field='name',
|
target_field='name',
|
||||||
naturalize_function=naturalize_interface,
|
naturalize_function=naturalize_interface,
|
||||||
max_length=100,
|
max_length=100,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfaceTypeChoices
|
choices=InterfaceTypeChoices
|
||||||
@ -314,19 +248,6 @@ class FrontPortTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
Template for a pass-through port on the front of a new Device.
|
Template for a pass-through port on the front of a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='frontport_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=64
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -348,9 +269,6 @@ class FrontPortTemplate(ComponentTemplateModel):
|
|||||||
('rear_port', 'rear_port_position'),
|
('rear_port', 'rear_port_position'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
||||||
# Validate rear port assignment
|
# Validate rear port assignment
|
||||||
@ -385,19 +303,6 @@ class RearPortTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
Template for a pass-through port on the rear of a new Device.
|
Template for a pass-through port on the rear of a new Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='rearport_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=64
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -411,9 +316,6 @@ class RearPortTemplate(ComponentTemplateModel):
|
|||||||
ordering = ('device_type', '_name')
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ('device_type', 'name')
|
unique_together = ('device_type', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def instantiate(self, device):
|
def instantiate(self, device):
|
||||||
return RearPort(
|
return RearPort(
|
||||||
device=device,
|
device=device,
|
||||||
@ -427,25 +329,6 @@ class DeviceBayTemplate(ComponentTemplateModel):
|
|||||||
"""
|
"""
|
||||||
A template for a DeviceBay to be created for a new parent Device.
|
A template for a DeviceBay to be created for a new parent Device.
|
||||||
"""
|
"""
|
||||||
device_type = models.ForeignKey(
|
|
||||||
to='dcim.DeviceType',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='device_bay_templates'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device_type', '_name')
|
ordering = ('device_type', '_name')
|
||||||
unique_together = ('device_type', 'name')
|
unique_together = ('device_type', 'name')
|
||||||
|
@ -36,6 +36,24 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class ComponentModel(models.Model):
|
class ComponentModel(models.Model):
|
||||||
|
device = models.ForeignKey(
|
||||||
|
to='dcim.Device',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='%(class)ss'
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=64
|
||||||
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
label = models.CharField(
|
||||||
|
max_length=64,
|
||||||
|
blank=True,
|
||||||
|
help_text="Physical label"
|
||||||
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True
|
blank=True
|
||||||
@ -233,24 +251,6 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
"""
|
"""
|
||||||
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
|
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='consoleports'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
@ -270,7 +270,7 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'description']
|
csv_headers = ['device', 'name', 'label', 'type', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
@ -283,6 +283,7 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.type,
|
self.type,
|
||||||
self.description,
|
self.description,
|
||||||
)
|
)
|
||||||
@ -297,24 +298,6 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
"""
|
"""
|
||||||
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
|
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='consoleserverports'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
@ -327,7 +310,7 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'description']
|
csv_headers = ['device', 'name', 'label', 'type', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
@ -340,6 +323,7 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.type,
|
self.type,
|
||||||
self.description,
|
self.description,
|
||||||
)
|
)
|
||||||
@ -354,24 +338,6 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
"""
|
"""
|
||||||
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
|
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='powerports'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
@ -410,7 +376,7 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'maximum_draw', 'allocated_draw', 'description']
|
csv_headers = ['device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
@ -423,6 +389,7 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.get_type_display(),
|
self.get_type_display(),
|
||||||
self.maximum_draw,
|
self.maximum_draw,
|
||||||
self.allocated_draw,
|
self.allocated_draw,
|
||||||
@ -519,24 +486,6 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
"""
|
"""
|
||||||
A physical power outlet (output) within a Device which provides power to a PowerPort.
|
A physical power outlet (output) within a Device which provides power to a PowerPort.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='poweroutlets'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
@ -562,7 +511,7 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'power_port', 'feed_leg', 'description']
|
csv_headers = ['device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
@ -575,6 +524,7 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.get_type_display(),
|
self.get_type_display(),
|
||||||
self.power_port.name if self.power_port else None,
|
self.power_port.name if self.power_port else None,
|
||||||
self.get_feed_leg_display(),
|
self.get_feed_leg_display(),
|
||||||
@ -595,15 +545,9 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class BaseInterface(models.Model):
|
class BaseInterface(models.Model):
|
||||||
name = models.CharField(
|
"""
|
||||||
max_length=64
|
Abstract base class for fields shared by dcim.Interface and virtualization.VMInterface.
|
||||||
)
|
"""
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
naturalize_function=naturalize_interface,
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
enabled = models.BooleanField(
|
enabled = models.BooleanField(
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
@ -633,18 +577,13 @@ class Interface(CableTermination, ComponentModel, BaseInterface):
|
|||||||
"""
|
"""
|
||||||
A network interface within a Device. A physical Interface can connect to exactly one other Interface.
|
A network interface within a Device. A physical Interface can connect to exactly one other Interface.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
# Override ComponentModel._name to specify naturalize_interface function
|
||||||
to='Device',
|
_name = NaturalOrderingField(
|
||||||
on_delete=models.CASCADE,
|
target_field='name',
|
||||||
related_name='interfaces',
|
naturalize_function=naturalize_interface,
|
||||||
null=True,
|
max_length=100,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
_connected_interface = models.OneToOneField(
|
_connected_interface = models.OneToOneField(
|
||||||
to='self',
|
to='self',
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
@ -703,7 +642,7 @@ class Interface(CableTermination, ComponentModel, BaseInterface):
|
|||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'device', 'name', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', 'description', 'mode',
|
'device', 'name', 'label', 'lag', 'type', 'enabled', 'mac_address', 'mtu', 'mgmt_only', 'description', 'mode',
|
||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -717,6 +656,7 @@ class Interface(CableTermination, ComponentModel, BaseInterface):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier if self.device else None,
|
self.device.identifier if self.device else None,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.lag.name if self.lag else None,
|
self.lag.name if self.lag else None,
|
||||||
self.get_type_display(),
|
self.get_type_display(),
|
||||||
self.enabled,
|
self.enabled,
|
||||||
@ -849,19 +789,6 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
"""
|
"""
|
||||||
A pass-through port on the front of a Device.
|
A pass-through port on the front of a Device.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='frontports'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=64
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -877,7 +804,7 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
|
csv_headers = ['device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
@ -886,9 +813,6 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
('rear_port', 'rear_port_position'),
|
('rear_port', 'rear_port_position'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('dcim:frontport', kwargs={'pk': self.pk})
|
return reverse('dcim:frontport', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
@ -896,6 +820,7 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.get_type_display(),
|
self.get_type_display(),
|
||||||
self.rear_port.name,
|
self.rear_port.name,
|
||||||
self.rear_port_position,
|
self.rear_port_position,
|
||||||
@ -924,19 +849,6 @@ class RearPort(CableTermination, ComponentModel):
|
|||||||
"""
|
"""
|
||||||
A pass-through port on the rear of a Device.
|
A pass-through port on the rear of a Device.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='rearports'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=64
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
@ -947,15 +859,12 @@ class RearPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'positions', 'description']
|
csv_headers = ['device', 'name', 'label', 'type', 'positions', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
unique_together = ('device', 'name')
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('dcim:rearport', kwargs={'pk': self.pk})
|
return reverse('dcim:rearport', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
@ -963,6 +872,7 @@ class RearPort(CableTermination, ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.get_type_display(),
|
self.get_type_display(),
|
||||||
self.positions,
|
self.positions,
|
||||||
self.description,
|
self.description,
|
||||||
@ -978,25 +888,6 @@ class DeviceBay(ComponentModel):
|
|||||||
"""
|
"""
|
||||||
An empty space within a Device which can house a child device
|
An empty space within a Device which can house a child device
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='device_bays'
|
|
||||||
)
|
|
||||||
name = models.CharField(
|
|
||||||
max_length=50,
|
|
||||||
verbose_name='Name'
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
label = models.CharField(
|
|
||||||
max_length=64,
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical label"
|
|
||||||
)
|
|
||||||
installed_device = models.OneToOneField(
|
installed_device = models.OneToOneField(
|
||||||
to='dcim.Device',
|
to='dcim.Device',
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
@ -1006,17 +897,12 @@ class DeviceBay(ComponentModel):
|
|||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'installed_device', 'description']
|
csv_headers = ['device', 'name', 'label', 'installed_device', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device', '_name')
|
ordering = ('device', '_name')
|
||||||
unique_together = ('device', 'name')
|
unique_together = ('device', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.label:
|
|
||||||
return '{} - {} ({})'.format(self.device.name, self.name, self.label)
|
|
||||||
return '{} - {}'.format(self.device.name, self.name)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('dcim:devicebay', kwargs={'pk': self.pk})
|
return reverse('dcim:devicebay', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
@ -1024,6 +910,7 @@ class DeviceBay(ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.identifier,
|
self.device.identifier,
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.installed_device.identifier if self.installed_device else None,
|
self.installed_device.identifier if self.installed_device else None,
|
||||||
self.description,
|
self.description,
|
||||||
)
|
)
|
||||||
@ -1061,11 +948,6 @@ class InventoryItem(ComponentModel):
|
|||||||
An InventoryItem represents a serialized piece of hardware within a Device, such as a line card or power supply.
|
An InventoryItem represents a serialized piece of hardware within a Device, such as a line card or power supply.
|
||||||
InventoryItems are used only for inventory purposes.
|
InventoryItems are used only for inventory purposes.
|
||||||
"""
|
"""
|
||||||
device = models.ForeignKey(
|
|
||||||
to='dcim.Device',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='inventory_items'
|
|
||||||
)
|
|
||||||
parent = models.ForeignKey(
|
parent = models.ForeignKey(
|
||||||
to='self',
|
to='self',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
@ -1073,15 +955,6 @@ class InventoryItem(ComponentModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
name = models.CharField(
|
|
||||||
max_length=50,
|
|
||||||
verbose_name='Name'
|
|
||||||
)
|
|
||||||
_name = NaturalOrderingField(
|
|
||||||
target_field='name',
|
|
||||||
max_length=100,
|
|
||||||
blank=True
|
|
||||||
)
|
|
||||||
manufacturer = models.ForeignKey(
|
manufacturer = models.ForeignKey(
|
||||||
to='dcim.Manufacturer',
|
to='dcim.Manufacturer',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -1116,16 +989,13 @@ class InventoryItem(ComponentModel):
|
|||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
||||||
]
|
]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('device__id', 'parent__id', '_name')
|
ordering = ('device__id', 'parent__id', '_name')
|
||||||
unique_together = ('device', 'parent', 'name')
|
unique_together = ('device', 'parent', 'name')
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('dcim:inventoryitem', kwargs={'pk': self.pk})
|
return reverse('dcim:inventoryitem', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
@ -1133,6 +1003,7 @@ class InventoryItem(ComponentModel):
|
|||||||
return (
|
return (
|
||||||
self.device.name or '{{{}}}'.format(self.device.pk),
|
self.device.name or '{{{}}}'.format(self.device.pk),
|
||||||
self.name,
|
self.name,
|
||||||
|
self.label,
|
||||||
self.manufacturer.name if self.manufacturer else None,
|
self.manufacturer.name if self.manufacturer else None,
|
||||||
self.part_id,
|
self.part_id,
|
||||||
self.serial,
|
self.serial,
|
||||||
|
@ -110,21 +110,6 @@ POWERPANEL_POWERFEED_COUNT = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_component_template_actions(model_name):
|
|
||||||
return """
|
|
||||||
{{% if perms.dcim.change_{model_name} %}}
|
|
||||||
<a href="{{% url 'dcim:{model_name}_edit' pk=record.pk %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-warning">
|
|
||||||
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
{{% endif %}}
|
|
||||||
{{% if perms.dcim.delete_{model_name} %}}
|
|
||||||
<a href="{{% url 'dcim:{model_name}_delete' pk=record.pk %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-danger">
|
|
||||||
<i class="glyphicon glyphicon-trash" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
{{% endif %}}
|
|
||||||
""".format(model_name=model_name).strip()
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Regions
|
# Regions
|
||||||
#
|
#
|
||||||
@ -401,10 +386,9 @@ class ComponentTemplateTable(BaseTable):
|
|||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplateTable(ComponentTemplateTable):
|
class ConsolePortTemplateTable(ComponentTemplateTable):
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('consoleporttemplate'),
|
model=ConsolePortTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -414,10 +398,9 @@ class ConsolePortTemplateTable(ComponentTemplateTable):
|
|||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateTable(ComponentTemplateTable):
|
class ConsoleServerPortTemplateTable(ComponentTemplateTable):
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('consoleserverporttemplate'),
|
model=ConsoleServerPortTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -427,10 +410,9 @@ class ConsoleServerPortTemplateTable(ComponentTemplateTable):
|
|||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateTable(ComponentTemplateTable):
|
class PowerPortTemplateTable(ComponentTemplateTable):
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('powerporttemplate'),
|
model=PowerPortTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -440,10 +422,9 @@ class PowerPortTemplateTable(ComponentTemplateTable):
|
|||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateTable(ComponentTemplateTable):
|
class PowerOutletTemplateTable(ComponentTemplateTable):
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('poweroutlettemplate'),
|
model=PowerOutletTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -456,10 +437,9 @@ class InterfaceTemplateTable(ComponentTemplateTable):
|
|||||||
mgmt_only = BooleanColumn(
|
mgmt_only = BooleanColumn(
|
||||||
verbose_name='Management Only'
|
verbose_name='Management Only'
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('interfacetemplate'),
|
model=InterfaceTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -472,10 +452,9 @@ class FrontPortTemplateTable(ComponentTemplateTable):
|
|||||||
rear_port_position = tables.Column(
|
rear_port_position = tables.Column(
|
||||||
verbose_name='Position'
|
verbose_name='Position'
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('frontporttemplate'),
|
model=FrontPortTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -485,10 +464,9 @@ class FrontPortTemplateTable(ComponentTemplateTable):
|
|||||||
|
|
||||||
|
|
||||||
class RearPortTemplateTable(ComponentTemplateTable):
|
class RearPortTemplateTable(ComponentTemplateTable):
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('rearporttemplate'),
|
model=RearPortTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -498,10 +476,9 @@ class RearPortTemplateTable(ComponentTemplateTable):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateTable(ComponentTemplateTable):
|
class DeviceBayTemplateTable(ComponentTemplateTable):
|
||||||
actions = tables.TemplateColumn(
|
actions = ButtonsColumn(
|
||||||
template_code=get_component_template_actions('devicebaytemplate'),
|
model=DeviceBayTemplate,
|
||||||
attrs={'td': {'class': 'text-right noprint'}},
|
buttons=('edit', 'delete')
|
||||||
verbose_name=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -784,9 +761,10 @@ class InventoryItemTable(DeviceComponentTable):
|
|||||||
class Meta(DeviceComponentTable.Meta):
|
class Meta(DeviceComponentTable.Meta):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'discovered'
|
'pk', 'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description',
|
||||||
|
'discovered',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag')
|
default_columns = ('pk', 'device', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.test import override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
@ -131,6 +132,7 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_get_site_graphs(self):
|
def test_get_site_graphs(self):
|
||||||
"""
|
"""
|
||||||
Test retrieval of Graphs assigned to Sites.
|
Test retrieval of Graphs assigned to Sites.
|
||||||
@ -900,6 +902,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_get_device_graphs(self):
|
def test_get_device_graphs(self):
|
||||||
"""
|
"""
|
||||||
Test retrieval of Graphs assigned to Devices.
|
Test retrieval of Graphs assigned to Devices.
|
||||||
@ -1156,6 +1159,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_get_interface_graphs(self):
|
def test_get_interface_graphs(self):
|
||||||
"""
|
"""
|
||||||
Test retrieval of Graphs assigned to Devices.
|
Test retrieval of Graphs assigned to Devices.
|
||||||
|
@ -4,6 +4,7 @@ import pytz
|
|||||||
import yaml
|
import yaml
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.test import override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from netaddr import EUI
|
from netaddr import EUI
|
||||||
|
|
||||||
@ -376,6 +377,7 @@ class DeviceTypeTestCase(
|
|||||||
'is_full_depth': False,
|
'is_full_depth': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_import_objects(self):
|
def test_import_objects(self):
|
||||||
"""
|
"""
|
||||||
Custom import test for YAML-based imports (versus CSV)
|
Custom import test for YAML-based imports (versus CSV)
|
||||||
@ -479,45 +481,45 @@ device-bays:
|
|||||||
self.assertEqual(dt.comments, 'test comment')
|
self.assertEqual(dt.comments, 'test comment')
|
||||||
|
|
||||||
# Verify all of the components were created
|
# Verify all of the components were created
|
||||||
self.assertEqual(dt.consoleport_templates.count(), 3)
|
self.assertEqual(dt.consoleporttemplates.count(), 3)
|
||||||
cp1 = ConsolePortTemplate.objects.first()
|
cp1 = ConsolePortTemplate.objects.first()
|
||||||
self.assertEqual(cp1.name, 'Console Port 1')
|
self.assertEqual(cp1.name, 'Console Port 1')
|
||||||
self.assertEqual(cp1.type, ConsolePortTypeChoices.TYPE_DE9)
|
self.assertEqual(cp1.type, ConsolePortTypeChoices.TYPE_DE9)
|
||||||
|
|
||||||
self.assertEqual(dt.consoleserverport_templates.count(), 3)
|
self.assertEqual(dt.consoleserverporttemplates.count(), 3)
|
||||||
csp1 = ConsoleServerPortTemplate.objects.first()
|
csp1 = ConsoleServerPortTemplate.objects.first()
|
||||||
self.assertEqual(csp1.name, 'Console Server Port 1')
|
self.assertEqual(csp1.name, 'Console Server Port 1')
|
||||||
self.assertEqual(csp1.type, ConsolePortTypeChoices.TYPE_RJ45)
|
self.assertEqual(csp1.type, ConsolePortTypeChoices.TYPE_RJ45)
|
||||||
|
|
||||||
self.assertEqual(dt.powerport_templates.count(), 3)
|
self.assertEqual(dt.powerporttemplates.count(), 3)
|
||||||
pp1 = PowerPortTemplate.objects.first()
|
pp1 = PowerPortTemplate.objects.first()
|
||||||
self.assertEqual(pp1.name, 'Power Port 1')
|
self.assertEqual(pp1.name, 'Power Port 1')
|
||||||
self.assertEqual(pp1.type, PowerPortTypeChoices.TYPE_IEC_C14)
|
self.assertEqual(pp1.type, PowerPortTypeChoices.TYPE_IEC_C14)
|
||||||
|
|
||||||
self.assertEqual(dt.poweroutlet_templates.count(), 3)
|
self.assertEqual(dt.poweroutlettemplates.count(), 3)
|
||||||
po1 = PowerOutletTemplate.objects.first()
|
po1 = PowerOutletTemplate.objects.first()
|
||||||
self.assertEqual(po1.name, 'Power Outlet 1')
|
self.assertEqual(po1.name, 'Power Outlet 1')
|
||||||
self.assertEqual(po1.type, PowerOutletTypeChoices.TYPE_IEC_C13)
|
self.assertEqual(po1.type, PowerOutletTypeChoices.TYPE_IEC_C13)
|
||||||
self.assertEqual(po1.power_port, pp1)
|
self.assertEqual(po1.power_port, pp1)
|
||||||
self.assertEqual(po1.feed_leg, PowerOutletFeedLegChoices.FEED_LEG_A)
|
self.assertEqual(po1.feed_leg, PowerOutletFeedLegChoices.FEED_LEG_A)
|
||||||
|
|
||||||
self.assertEqual(dt.interface_templates.count(), 3)
|
self.assertEqual(dt.interfacetemplates.count(), 3)
|
||||||
iface1 = InterfaceTemplate.objects.first()
|
iface1 = InterfaceTemplate.objects.first()
|
||||||
self.assertEqual(iface1.name, 'Interface 1')
|
self.assertEqual(iface1.name, 'Interface 1')
|
||||||
self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
|
self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
|
||||||
self.assertTrue(iface1.mgmt_only)
|
self.assertTrue(iface1.mgmt_only)
|
||||||
|
|
||||||
self.assertEqual(dt.rearport_templates.count(), 3)
|
self.assertEqual(dt.rearporttemplates.count(), 3)
|
||||||
rp1 = RearPortTemplate.objects.first()
|
rp1 = RearPortTemplate.objects.first()
|
||||||
self.assertEqual(rp1.name, 'Rear Port 1')
|
self.assertEqual(rp1.name, 'Rear Port 1')
|
||||||
|
|
||||||
self.assertEqual(dt.frontport_templates.count(), 3)
|
self.assertEqual(dt.frontporttemplates.count(), 3)
|
||||||
fp1 = FrontPortTemplate.objects.first()
|
fp1 = FrontPortTemplate.objects.first()
|
||||||
self.assertEqual(fp1.name, 'Front Port 1')
|
self.assertEqual(fp1.name, 'Front Port 1')
|
||||||
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.device_bay_templates.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')
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ urlpatterns = [
|
|||||||
# Console port templates
|
# Console port templates
|
||||||
path('console-port-templates/add/', views.ConsolePortTemplateCreateView.as_view(), name='consoleporttemplate_add'),
|
path('console-port-templates/add/', views.ConsolePortTemplateCreateView.as_view(), name='consoleporttemplate_add'),
|
||||||
path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'),
|
path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'),
|
||||||
|
path('console-port-templates/rename/', views.ConsolePortTemplateBulkRenameView.as_view(), name='consoleporttemplate_bulk_rename'),
|
||||||
path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'),
|
path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'),
|
||||||
path('console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
|
path('console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
|
||||||
path('console-port-templates/<int:pk>/delete/', views.ConsolePortTemplateDeleteView.as_view(), name='consoleporttemplate_delete'),
|
path('console-port-templates/<int:pk>/delete/', views.ConsolePortTemplateDeleteView.as_view(), name='consoleporttemplate_delete'),
|
||||||
@ -105,6 +106,7 @@ urlpatterns = [
|
|||||||
# Console server port templates
|
# Console server port templates
|
||||||
path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'),
|
path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'),
|
||||||
path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'),
|
path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'),
|
||||||
|
path('console-server-port-templates/rename/', views.ConsoleServerPortTemplateBulkRenameView.as_view(), name='consoleserverporttemplate_bulk_rename'),
|
||||||
path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'),
|
path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'),
|
||||||
path('console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
|
path('console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
|
||||||
path('console-server-port-templates/<int:pk>/delete/', views.ConsoleServerPortTemplateDeleteView.as_view(), name='consoleserverporttemplate_delete'),
|
path('console-server-port-templates/<int:pk>/delete/', views.ConsoleServerPortTemplateDeleteView.as_view(), name='consoleserverporttemplate_delete'),
|
||||||
@ -112,6 +114,7 @@ urlpatterns = [
|
|||||||
# Power port templates
|
# Power port templates
|
||||||
path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'),
|
path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'),
|
||||||
path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'),
|
path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'),
|
||||||
|
path('power-port-templates/rename/', views.PowerPortTemplateBulkRenameView.as_view(), name='powerporttemplate_bulk_rename'),
|
||||||
path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'),
|
path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'),
|
||||||
path('power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
|
path('power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
|
||||||
path('power-port-templates/<int:pk>/delete/', views.PowerPortTemplateDeleteView.as_view(), name='powerporttemplate_delete'),
|
path('power-port-templates/<int:pk>/delete/', views.PowerPortTemplateDeleteView.as_view(), name='powerporttemplate_delete'),
|
||||||
@ -119,6 +122,7 @@ urlpatterns = [
|
|||||||
# Power outlet templates
|
# Power outlet templates
|
||||||
path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'),
|
path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'),
|
||||||
path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'),
|
path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'),
|
||||||
|
path('power-outlet-templates/rename/', views.PowerOutletTemplateBulkRenameView.as_view(), name='poweroutlettemplate_bulk_rename'),
|
||||||
path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'),
|
path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'),
|
||||||
path('power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
|
path('power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
|
||||||
path('power-outlet-templates/<int:pk>/delete/', views.PowerOutletTemplateDeleteView.as_view(), name='poweroutlettemplate_delete'),
|
path('power-outlet-templates/<int:pk>/delete/', views.PowerOutletTemplateDeleteView.as_view(), name='poweroutlettemplate_delete'),
|
||||||
@ -126,6 +130,7 @@ urlpatterns = [
|
|||||||
# Interface templates
|
# Interface templates
|
||||||
path('interface-templates/add/', views.InterfaceTemplateCreateView.as_view(), name='interfacetemplate_add'),
|
path('interface-templates/add/', views.InterfaceTemplateCreateView.as_view(), name='interfacetemplate_add'),
|
||||||
path('interface-templates/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='interfacetemplate_bulk_edit'),
|
path('interface-templates/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='interfacetemplate_bulk_edit'),
|
||||||
|
path('interface-templates/rename/', views.InterfaceTemplateBulkRenameView.as_view(), name='interfacetemplate_bulk_rename'),
|
||||||
path('interface-templates/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='interfacetemplate_bulk_delete'),
|
path('interface-templates/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='interfacetemplate_bulk_delete'),
|
||||||
path('interface-templates/<int:pk>/edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
|
path('interface-templates/<int:pk>/edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
|
||||||
path('interface-templates/<int:pk>/delete/', views.InterfaceTemplateDeleteView.as_view(), name='interfacetemplate_delete'),
|
path('interface-templates/<int:pk>/delete/', views.InterfaceTemplateDeleteView.as_view(), name='interfacetemplate_delete'),
|
||||||
@ -133,6 +138,7 @@ urlpatterns = [
|
|||||||
# Front port templates
|
# Front port templates
|
||||||
path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'),
|
path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'),
|
||||||
path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'),
|
path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'),
|
||||||
|
path('front-port-templates/rename/', views.FrontPortTemplateBulkRenameView.as_view(), name='frontporttemplate_bulk_rename'),
|
||||||
path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'),
|
path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'),
|
||||||
path('front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
|
path('front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
|
||||||
path('front-port-templates/<int:pk>/delete/', views.FrontPortTemplateDeleteView.as_view(), name='frontporttemplate_delete'),
|
path('front-port-templates/<int:pk>/delete/', views.FrontPortTemplateDeleteView.as_view(), name='frontporttemplate_delete'),
|
||||||
@ -140,6 +146,7 @@ urlpatterns = [
|
|||||||
# Rear port templates
|
# Rear port templates
|
||||||
path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'),
|
path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'),
|
||||||
path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'),
|
path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'),
|
||||||
|
path('rear-port-templates/rename/', views.RearPortTemplateBulkRenameView.as_view(), name='rearporttemplate_bulk_rename'),
|
||||||
path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'),
|
path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'),
|
||||||
path('rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
|
path('rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
|
||||||
path('rear-port-templates/<int:pk>/delete/', views.RearPortTemplateDeleteView.as_view(), name='rearporttemplate_delete'),
|
path('rear-port-templates/<int:pk>/delete/', views.RearPortTemplateDeleteView.as_view(), name='rearporttemplate_delete'),
|
||||||
@ -147,6 +154,7 @@ urlpatterns = [
|
|||||||
# Device bay templates
|
# Device bay templates
|
||||||
path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'),
|
path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'),
|
||||||
path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'),
|
path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'),
|
||||||
|
path('device-bay-templates/rename/', views.DeviceBayTemplateBulkRenameView.as_view(), name='devicebaytemplate_bulk_rename'),
|
||||||
path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'),
|
path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'),
|
||||||
path('device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
|
path('device-bay-templates/<int:pk>/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'),
|
||||||
|
@ -640,6 +640,10 @@ class ConsolePortTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.ConsolePortTemplateBulkEditForm
|
form = forms.ConsolePortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class ConsolePortTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = ConsolePortTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class ConsolePortTemplateBulkDeleteView(BulkDeleteView):
|
class ConsolePortTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = ConsolePortTemplate.objects.all()
|
queryset = ConsolePortTemplate.objects.all()
|
||||||
table = tables.ConsolePortTemplateTable
|
table = tables.ConsolePortTemplateTable
|
||||||
@ -671,6 +675,10 @@ class ConsoleServerPortTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.ConsoleServerPortTemplateBulkEditForm
|
form = forms.ConsoleServerPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class ConsoleServerPortTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = ConsoleServerPortTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateBulkDeleteView(BulkDeleteView):
|
class ConsoleServerPortTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = ConsoleServerPortTemplate.objects.all()
|
queryset = ConsoleServerPortTemplate.objects.all()
|
||||||
table = tables.ConsoleServerPortTemplateTable
|
table = tables.ConsoleServerPortTemplateTable
|
||||||
@ -702,6 +710,10 @@ class PowerPortTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.PowerPortTemplateBulkEditForm
|
form = forms.PowerPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class PowerPortTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = PowerPortTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateBulkDeleteView(BulkDeleteView):
|
class PowerPortTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = PowerPortTemplate.objects.all()
|
queryset = PowerPortTemplate.objects.all()
|
||||||
table = tables.PowerPortTemplateTable
|
table = tables.PowerPortTemplateTable
|
||||||
@ -733,6 +745,10 @@ class PowerOutletTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.PowerOutletTemplateBulkEditForm
|
form = forms.PowerOutletTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class PowerOutletTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = PowerOutletTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateBulkDeleteView(BulkDeleteView):
|
class PowerOutletTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = PowerOutletTemplate.objects.all()
|
queryset = PowerOutletTemplate.objects.all()
|
||||||
table = tables.PowerOutletTemplateTable
|
table = tables.PowerOutletTemplateTable
|
||||||
@ -764,6 +780,10 @@ class InterfaceTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.InterfaceTemplateBulkEditForm
|
form = forms.InterfaceTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = InterfaceTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTemplateBulkDeleteView(BulkDeleteView):
|
class InterfaceTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = InterfaceTemplate.objects.all()
|
queryset = InterfaceTemplate.objects.all()
|
||||||
table = tables.InterfaceTemplateTable
|
table = tables.InterfaceTemplateTable
|
||||||
@ -795,6 +815,10 @@ class FrontPortTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.FrontPortTemplateBulkEditForm
|
form = forms.FrontPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = FrontPortTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateBulkDeleteView(BulkDeleteView):
|
class FrontPortTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = FrontPortTemplate.objects.all()
|
queryset = FrontPortTemplate.objects.all()
|
||||||
table = tables.FrontPortTemplateTable
|
table = tables.FrontPortTemplateTable
|
||||||
@ -826,6 +850,10 @@ class RearPortTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.RearPortTemplateBulkEditForm
|
form = forms.RearPortTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class RearPortTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = RearPortTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateBulkDeleteView(BulkDeleteView):
|
class RearPortTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = RearPortTemplate.objects.all()
|
queryset = RearPortTemplate.objects.all()
|
||||||
table = tables.RearPortTemplateTable
|
table = tables.RearPortTemplateTable
|
||||||
@ -857,6 +885,10 @@ class DeviceBayTemplateBulkEditView(BulkEditView):
|
|||||||
form = forms.DeviceBayTemplateBulkEditForm
|
form = forms.DeviceBayTemplateBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBayTemplateBulkRenameView(BulkRenameView):
|
||||||
|
queryset = DeviceBayTemplate.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateBulkDeleteView(BulkDeleteView):
|
class DeviceBayTemplateBulkDeleteView(BulkDeleteView):
|
||||||
queryset = DeviceBayTemplate.objects.all()
|
queryset = DeviceBayTemplate.objects.all()
|
||||||
table = tables.DeviceBayTemplateTable
|
table = tables.DeviceBayTemplateTable
|
||||||
@ -952,7 +984,7 @@ class DeviceView(ObjectView):
|
|||||||
vc_members = []
|
vc_members = []
|
||||||
|
|
||||||
# Console ports
|
# Console ports
|
||||||
console_ports = ConsolePort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
||||||
'connected_endpoint__device', 'cable',
|
'connected_endpoint__device', 'cable',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -964,7 +996,7 @@ class DeviceView(ObjectView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Power ports
|
# Power ports
|
||||||
power_ports = PowerPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
||||||
'_connected_poweroutlet__device', 'cable',
|
'_connected_poweroutlet__device', 'cable',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -982,15 +1014,15 @@ class DeviceView(ObjectView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Front ports
|
# Front ports
|
||||||
front_ports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
||||||
'rear_port', 'cable',
|
'rear_port', 'cable',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Rear ports
|
# Rear ports
|
||||||
rear_ports = RearPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related('cable')
|
rearports = RearPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related('cable')
|
||||||
|
|
||||||
# Device bays
|
# Device bays
|
||||||
device_bays = DeviceBay.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
|
||||||
'installed_device__device_type__manufacturer',
|
'installed_device__device_type__manufacturer',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1011,14 +1043,14 @@ class DeviceView(ObjectView):
|
|||||||
|
|
||||||
return render(request, 'dcim/device.html', {
|
return render(request, 'dcim/device.html', {
|
||||||
'device': device,
|
'device': device,
|
||||||
'console_ports': console_ports,
|
'consoleports': consoleports,
|
||||||
'consoleserverports': consoleserverports,
|
'consoleserverports': consoleserverports,
|
||||||
'power_ports': power_ports,
|
'powerports': powerports,
|
||||||
'poweroutlets': poweroutlets,
|
'poweroutlets': poweroutlets,
|
||||||
'interfaces': interfaces,
|
'interfaces': interfaces,
|
||||||
'device_bays': device_bays,
|
'devicebays': devicebays,
|
||||||
'front_ports': front_ports,
|
'frontports': frontports,
|
||||||
'rear_ports': rear_ports,
|
'rearports': rearports,
|
||||||
'services': services,
|
'services': services,
|
||||||
'secrets': secrets,
|
'secrets': secrets,
|
||||||
'vc_members': vc_members,
|
'vc_members': vc_members,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
from django.test import override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
|
||||||
@ -96,6 +97,7 @@ class SecretTestCase(
|
|||||||
self.session_key = SessionKey(userkey=userkey)
|
self.session_key = SessionKey(userkey=userkey)
|
||||||
self.session_key.save(master_key)
|
self.session_key.save(master_key)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
def test_import_objects(self):
|
def test_import_objects(self):
|
||||||
self.add_permissions('secrets.add_secret')
|
self.add_permissions('secrets.add_secret')
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li role="presentation"{% if active_tab == 'inventory' %} class="active"{% endif %}>
|
<li role="presentation"{% if active_tab == 'inventory' %} class="active"{% endif %}>
|
||||||
<a href="{% url 'dcim:device_inventory' pk=device.pk %}">
|
<a href="{% url 'dcim:device_inventory' pk=device.pk %}">
|
||||||
Inventory <span class="badge">{{ device.inventory_items.count }}</span>
|
Inventory <span class="badge">{{ device.inventoryitems.unrestricted.count }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if perms.dcim.napalm_read_device %}
|
{% if perms.dcim.napalm_read_device %}
|
||||||
@ -329,86 +329,6 @@
|
|||||||
{% plugin_left_page device %}
|
{% plugin_left_page device %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% if console_ports %}
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong>Console Ports</strong>
|
|
||||||
</div>
|
|
||||||
<table class="table table-hover panel-body component-list">
|
|
||||||
{% for cp in console_ports %}
|
|
||||||
{% include 'dcim/inc/consoleport.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<div class="panel-footer noprint">
|
|
||||||
{% if console_ports and perms.dcim.change_consoleport %}
|
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if console_ports and perms.dcim.delete_consoleport %}
|
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if console_ports and perms.dcim.add_consoleport %}
|
|
||||||
<div class="pull-right">
|
|
||||||
<a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% if power_ports %}
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong>Power Ports</strong>
|
|
||||||
</div>
|
|
||||||
<table class="table table-hover panel-body component-list">
|
|
||||||
{% for pp in power_ports %}
|
|
||||||
{% include 'dcim/inc/powerport.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<div class="panel-footer noprint">
|
|
||||||
{% if power_ports and perms.dcim.change_powerport %}
|
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:powerport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:powerport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if power_ports and perms.dcim.delete_powerport %}
|
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if power_ports and perms.dcim.add_powerport %}
|
|
||||||
<div class="pull-right">
|
|
||||||
<a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% if power_ports and poweroutlets %}
|
{% if power_ports and poweroutlets %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
@ -554,355 +474,490 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% if device_bays or device.device_type.is_parent_device %}
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<form method="post">
|
<li role="presentation" class="active">
|
||||||
{% csrf_token %}
|
<a href="#interfaces" role="tab" data-toggle="tab">Interfaces {% badge interfaces|length %}</a>
|
||||||
<div class="panel panel-default">
|
</li>
|
||||||
<div class="panel-heading">
|
<li role="presentation">
|
||||||
<strong>Device Bays</strong>
|
<a href="#frontports" role="tab" data-toggle="tab">Front Ports {% badge frontports|length %}</a>
|
||||||
</div>
|
</li>
|
||||||
<table class="table table-hover table-headings panel-body component-list">
|
<li role="presentation">
|
||||||
<thead>
|
<a href="#rearports" role="tab" data-toggle="tab">Rear Ports {% badge rearports|length %}</a>
|
||||||
<tr>
|
</li>
|
||||||
{% if perms.dcim.change_devicebay or perms.dcim.delete_devicebay %}
|
<li role="presentation">
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
<a href="#consoleports" role="tab" data-toggle="tab">Console Ports {% badge consoleports|length %}</a>
|
||||||
{% endif %}
|
</li>
|
||||||
<th>Name</th>
|
<li role="presentation">
|
||||||
<th>Status</th>
|
<a href="#consoleserverports" role="tab" data-toggle="tab">Console Server Ports {% badge consoleserverports|length %}</a>
|
||||||
<th>Description</th>
|
</li>
|
||||||
<th colspan="2">Installed Device</th>
|
<li role="presentation">
|
||||||
<th></th>
|
<a href="#powerports" role="tab" data-toggle="tab">Power Ports {% badge powerports|length %}</a>
|
||||||
</tr>
|
</li>
|
||||||
</thead>
|
<li role="presentation">
|
||||||
<tbody>
|
<a href="#poweroutlets" role="tab" data-toggle="tab">Power Outlets {% badge poweroutlets|length %}</a>
|
||||||
{% for devicebay in device_bays %}
|
</li>
|
||||||
{% include 'dcim/inc/devicebay.html' %}
|
<li role="presentation">
|
||||||
{% empty %}
|
<a href="#devicebays" role="tab" data-toggle="tab">Device Bays {% badge devicebays|length %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="interfaces">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Interfaces</strong>
|
||||||
|
<div class="pull-right noprint">
|
||||||
|
<button class="btn btn-default btn-xs toggle-ips" selected="selected">
|
||||||
|
<span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 pull-right noprint">
|
||||||
|
<input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table id="interfaces_table" class="table table-hover table-headings panel-body component-list">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="text-center text-muted">— No device bays defined —</td>
|
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
|
<th>LAG</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>MTU</th>
|
||||||
|
<th>Mode</th>
|
||||||
|
<th>Cable</th>
|
||||||
|
<th colspan="2">Connection</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{% for iface in interfaces %}
|
||||||
<div class="panel-footer noprint">
|
{% include 'dcim/inc/interface.html' %}
|
||||||
{% if device_bays and perms.dcim.change_devicebay %}
|
{% endfor %}
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
</tbody>
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
</table>
|
||||||
</button>
|
<div class="panel-footer noprint">
|
||||||
{% endif %}
|
{% if interfaces and perms.dcim.change_interface %}
|
||||||
{% if device_bays and perms.dcim.delete_devicebay %}
|
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
|
</button>
|
||||||
</button>
|
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
{% endif %}
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
{% if perms.dcim.add_devicebay %}
|
</button>
|
||||||
<div class="pull-right">
|
{% endif %}
|
||||||
<a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
{% if interfaces and perms.dcim.change_interface %}
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
</a>
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
</div>
|
</button>
|
||||||
<div class="clearfix"></div>
|
{% endif %}
|
||||||
{% endif %}
|
{% if interfaces and perms.dcim.delete_interface %}
|
||||||
</div>
|
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
</div>
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</form>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if interfaces %}
|
{% if perms.dcim.add_interface %}
|
||||||
<form method="post">
|
<div class="pull-right">
|
||||||
{% csrf_token %}
|
<a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<div class="panel panel-default">
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
|
||||||
<div class="panel-heading">
|
</a>
|
||||||
<strong>Interfaces</strong>
|
</div>
|
||||||
<div class="pull-right noprint">
|
<div class="clearfix"></div>
|
||||||
<button class="btn btn-default btn-xs toggle-ips" selected="selected">
|
{% endif %}
|
||||||
<span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
|
</div>
|
||||||
</button>
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="frontports">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Front Ports</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2 pull-right noprint">
|
<table class="table table-hover table-headings panel-body component-list">
|
||||||
<input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if perms.dcim.change_frontport or perms.dcim.delete_frontport %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Rear Port</th>
|
||||||
|
<th>Position</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Cable</th>
|
||||||
|
<th colspan="2">Connection</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for frontport in frontports %}
|
||||||
|
{% include 'dcim/inc/frontport.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="panel-footer noprint">
|
||||||
|
{% if frontports and perms.dcim.change_frontport %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:frontport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:frontport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if frontports and perms.dcim.delete_frontport %}
|
||||||
|
<button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_frontport %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add front ports
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table id="interfaces_table" class="table table-hover table-headings panel-body component-list">
|
</form>
|
||||||
<thead>
|
</div>
|
||||||
<tr>
|
<div role="tabpanel" class="tab-pane" id="rearports">
|
||||||
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
|
<form method="post">
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
{% csrf_token %}
|
||||||
{% endif %}
|
<div class="panel panel-default">
|
||||||
<th>Name</th>
|
<div class="panel-heading">
|
||||||
<th>LAG</th>
|
<strong>Rear Ports</strong>
|
||||||
<th>Description</th>
|
</div>
|
||||||
<th>MTU</th>
|
<table class="table table-hover table-headings panel-body component-list">
|
||||||
<th>Mode</th>
|
<thead>
|
||||||
<th>Cable</th>
|
<tr>
|
||||||
<th colspan="2">Connection</th>
|
{% if perms.dcim.change_rearport or perms.dcim.delete_rearport %}
|
||||||
<th></th>
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
</tr>
|
{% endif %}
|
||||||
</thead>
|
<th>Name</th>
|
||||||
<tbody>
|
<th>Type</th>
|
||||||
{% for iface in interfaces %}
|
<th>Positions</th>
|
||||||
{% include 'dcim/inc/interface.html' %}
|
<th>Description</th>
|
||||||
{% endfor %}
|
<th>Cable</th>
|
||||||
</tbody>
|
<th colspan="2">Connection</th>
|
||||||
</table>
|
<th></th>
|
||||||
<div class="panel-footer noprint">
|
</tr>
|
||||||
{% if interfaces and perms.dcim.change_interface %}
|
</thead>
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<tbody>
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
{% for rearport in rearports %}
|
||||||
</button>
|
{% include 'dcim/inc/rearport.html' %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
{% endfor %}
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
</tbody>
|
||||||
</button>
|
</table>
|
||||||
{% endif %}
|
<div class="panel-footer noprint">
|
||||||
{% if interfaces and perms.dcim.change_interface %}
|
{% if rearports and perms.dcim.change_rearport %}
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
<button type="submit" name="_edit" formaction="{% url 'dcim:rearport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
{% if interfaces and perms.dcim.delete_interface %}
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
</button>
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:rearport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
</button>
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
{% endif %}
|
</button>
|
||||||
{% if perms.dcim.add_interface %}
|
{% endif %}
|
||||||
<div class="pull-right">
|
{% if rearports and perms.dcim.delete_rearport %}
|
||||||
<a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
<button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</a>
|
</button>
|
||||||
</div>
|
{% endif %}
|
||||||
<div class="clearfix"></div>
|
{% if perms.dcim.add_rearport %}
|
||||||
{% endif %}
|
<div class="pull-right">
|
||||||
</div>
|
<a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
</div>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add rear ports
|
||||||
</form>
|
</a>
|
||||||
{% endif %}
|
</div>
|
||||||
{% if consoleserverports %}
|
<div class="clearfix"></div>
|
||||||
<form method="post">
|
{% endif %}
|
||||||
{% csrf_token %}
|
</div>
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong>Console Server Ports</strong>
|
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover table-headings panel-body component-list">
|
</form>
|
||||||
<thead>
|
</div>
|
||||||
<tr>
|
<div role="tabpanel" class="tab-pane" id="consoleports">
|
||||||
{% if perms.dcim.change_consoleserverport or perms.dcim.delete_consoleserverport %}
|
<form method="post">
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
{% csrf_token %}
|
||||||
{% endif %}
|
<div class="panel panel-default">
|
||||||
<th>Name</th>
|
<div class="panel-heading">
|
||||||
|
<strong>Console Ports</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover panel-body component-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if perms.dcim.change_consoleport or perms.dcim.delete_consoleport %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Cable</th>
|
||||||
|
<th colspan="2">Connection</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for cp in consoleports %}
|
||||||
|
{% include 'dcim/inc/consoleport.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
<div class="panel-footer noprint">
|
||||||
|
{% if consoleports and perms.dcim.change_consoleport %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if consoleports and perms.dcim.delete_consoleport %}
|
||||||
|
<button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if consoleports and perms.dcim.add_consoleport %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="consoleserverports">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Console Server Ports</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover table-headings panel-body component-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if perms.dcim.change_consoleserverport or perms.dcim.delete_consoleserverport %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Cable</th>
|
||||||
|
<th colspan="2">Connection</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for csp in consoleserverports %}
|
||||||
|
{% include 'dcim/inc/consoleserverport.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="panel-footer noprint">
|
||||||
|
{% if consoleserverports and perms.dcim.change_consoleport %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if consoleserverports and perms.dcim.delete_consoleserverport %}
|
||||||
|
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_consoleserverport %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="powerports">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Power Ports</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover panel-body component-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if perms.dcim.change_consoleport or perms.dcim.delete_consoleport %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Draw</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Cable</th>
|
||||||
|
<th colspan="2">Connection</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for pp in powerports %}
|
||||||
|
{% include 'dcim/inc/powerport.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
<div class="panel-footer noprint">
|
||||||
|
{% if powerports and perms.dcim.change_powerport %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:powerport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:powerport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if powerports and perms.dcim.delete_powerport %}
|
||||||
|
<button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if powerports and perms.dcim.add_powerport %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="poweroutlets">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Power Outlets</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover table-headings panel-body component-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if perms.dcim.change_poweroutlet or perms.dcim.delete_poweroutlet %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Description</th>
|
<th>Input/Leg</th>
|
||||||
<th>Cable</th>
|
<th>Description</th>
|
||||||
<th colspan="2">Connection</th>
|
<th>Cable</th>
|
||||||
<th></th>
|
<th colspan="3">Connection</th>
|
||||||
</tr>
|
<th></th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{% for csp in consoleserverports %}
|
<tbody>
|
||||||
{% include 'dcim/inc/consoleserverport.html' %}
|
{% for po in poweroutlets %}
|
||||||
{% endfor %}
|
{% include 'dcim/inc/poweroutlet.html' %}
|
||||||
</tbody>
|
{% endfor %}
|
||||||
</table>
|
</tbody>
|
||||||
<div class="panel-footer noprint">
|
</table>
|
||||||
{% if consoleserverports and perms.dcim.change_consoleport %}
|
<div class="panel-footer noprint">
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
{% if poweroutlets and perms.dcim.change_powerport %}
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
</button>
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
</button>
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
<button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
</button>
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
</button>
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
</button>
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
{% endif %}
|
</button>
|
||||||
{% if consoleserverports and perms.dcim.delete_consoleserverport %}
|
{% endif %}
|
||||||
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
{% if poweroutlets and perms.dcim.delete_poweroutlet %}
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
</button>
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
{% endif %}
|
</button>
|
||||||
{% if perms.dcim.add_consoleserverport %}
|
{% endif %}
|
||||||
<div class="pull-right">
|
{% if perms.dcim.add_poweroutlet %}
|
||||||
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
<div class="pull-right">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
|
<a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
</a>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
|
||||||
</div>
|
</a>
|
||||||
<div class="clearfix"></div>
|
</div>
|
||||||
{% endif %}
|
<div class="clearfix"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
</div>
|
||||||
{% endif %}
|
<div role="tabpanel" class="tab-pane" id="devicebays">
|
||||||
{% if poweroutlets %}
|
<form method="post">
|
||||||
<form method="post">
|
{% csrf_token %}
|
||||||
{% csrf_token %}
|
<div class="panel panel-default">
|
||||||
<div class="panel panel-default">
|
<div class="panel-heading">
|
||||||
<div class="panel-heading">
|
<strong>Device Bays</strong>
|
||||||
<strong>Power Outlets</strong>
|
</div>
|
||||||
|
<table class="table table-hover table-headings panel-body component-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% if perms.dcim.change_devicebay or perms.dcim.delete_devicebay %}
|
||||||
|
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
||||||
|
{% endif %}
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th colspan="2">Installed Device</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for devicebay in devicebays %}
|
||||||
|
{% include 'dcim/inc/devicebay.html' %}
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" class="text-center text-muted">— No device bays defined —</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="panel-footer noprint">
|
||||||
|
{% if devicebays and perms.dcim.change_devicebay %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if devicebays and perms.dcim.delete_devicebay %}
|
||||||
|
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.dcim.add_devicebay %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover table-headings panel-body component-list">
|
</form>
|
||||||
<thead>
|
</div>
|
||||||
<tr>
|
</div>
|
||||||
{% if perms.dcim.change_poweroutlet or perms.dcim.delete_poweroutlet %}
|
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
|
||||||
{% endif %}
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Input/Leg</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Cable</th>
|
|
||||||
<th colspan="3">Connection</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for po in poweroutlets %}
|
|
||||||
{% include 'dcim/inc/poweroutlet.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="panel-footer noprint">
|
|
||||||
{% if poweroutlets and perms.dcim.change_powerport %}
|
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if poweroutlets and perms.dcim.delete_poweroutlet %}
|
|
||||||
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.dcim.add_poweroutlet %}
|
|
||||||
<div class="pull-right">
|
|
||||||
<a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% if front_ports %}
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong>Front Ports</strong>
|
|
||||||
</div>
|
|
||||||
<table class="table table-hover table-headings panel-body component-list">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% if perms.dcim.change_frontport or perms.dcim.delete_frontport %}
|
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
|
||||||
{% endif %}
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Rear Port</th>
|
|
||||||
<th>Position</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Cable</th>
|
|
||||||
<th colspan="2">Connection</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for frontport in front_ports %}
|
|
||||||
{% include 'dcim/inc/frontport.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="panel-footer noprint">
|
|
||||||
{% if front_ports and perms.dcim.change_frontport %}
|
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:frontport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:frontport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:frontport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if front_ports and perms.dcim.delete_frontport %}
|
|
||||||
<button type="submit" formaction="{% url 'dcim:frontport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.dcim.add_frontport %}
|
|
||||||
<div class="pull-right">
|
|
||||||
<a href="{% url 'dcim:frontport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add front ports
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
{% if rear_ports %}
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<strong>Rear Ports</strong>
|
|
||||||
</div>
|
|
||||||
<table class="table table-hover table-headings panel-body component-list">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% if perms.dcim.change_rearport or perms.dcim.delete_rearport %}
|
|
||||||
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
|
|
||||||
{% endif %}
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Positions</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Cable</th>
|
|
||||||
<th colspan="2">Connection</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for rearport in rear_ports %}
|
|
||||||
{% include 'dcim/inc/rearport.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="panel-footer noprint">
|
|
||||||
{% if rear_ports and perms.dcim.change_rearport %}
|
|
||||||
<button type="submit" name="_rename" formaction="{% url 'dcim:rearport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:rearport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
|
||||||
</button>
|
|
||||||
<button type="submit" name="_disconnect" formaction="{% url 'dcim:rearport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if rear_ports and perms.dcim.delete_rearport %}
|
|
||||||
<button type="submit" formaction="{% url 'dcim:rearport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.dcim.add_rearport %}
|
|
||||||
<div class="pull-right">
|
|
||||||
<a href="{% url 'dcim:rearport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
|
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add rear ports
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% include 'inc/modal.html' with name='graphs' title='Graphs' %}
|
{% include 'inc/modal.html' with name='graphs' title='Graphs' %}
|
||||||
|
@ -63,149 +63,157 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Chassis</strong>
|
<strong>Chassis</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover panel-body attr-table">
|
||||||
|
<tr>
|
||||||
|
<td>Manufacturer</td>
|
||||||
|
<td><a href="{% url 'dcim:devicetype_list' %}?manufacturer={{ devicetype.manufacturer.slug }}">{{ devicetype.manufacturer }}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Model Name</td>
|
||||||
|
<td>
|
||||||
|
{{ devicetype.model }}<br/>
|
||||||
|
<small class="text-muted">{{ devicetype.slug }}</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Part Number</td>
|
||||||
|
<td>{{ devicetype.part_number|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Height (U)</td>
|
||||||
|
<td>{{ devicetype.u_height }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Full Depth</td>
|
||||||
|
<td>
|
||||||
|
{% if devicetype.is_full_depth %}
|
||||||
|
<i class="glyphicon glyphicon-ok text-success" title="Yes"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="glyphicon glyphicon-remove text-danger" title="No"></i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Parent/Child</td>
|
||||||
|
<td>
|
||||||
|
{{ devicetype.get_subdevice_role_display|placeholder }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Front Image</td>
|
||||||
|
<td>
|
||||||
|
{% if devicetype.front_image %}
|
||||||
|
<a href="{{ devicetype.front_image.url }}">
|
||||||
|
<img src="{{ devicetype.front_image.url }}" alt="{{ devicetype.front_image.name }}" class="img-responsive" />
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">—</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Rear Image</td>
|
||||||
|
<td>
|
||||||
|
{% if devicetype.rear_image %}
|
||||||
|
<a href="{{ devicetype.rear_image.url }}">
|
||||||
|
<img src="{{ devicetype.rear_image.url }}" alt="{{ devicetype.rear_image.name }}" class="img-responsive" />
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">—</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Instances</td>
|
||||||
|
<td><a href="{% url 'dcim:device_list' %}?device_type_id={{ devicetype.pk }}">{{ instance_count }}</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover panel-body attr-table">
|
{% plugin_left_page devicetype %}
|
||||||
<tr>
|
|
||||||
<td>Manufacturer</td>
|
|
||||||
<td><a href="{% url 'dcim:devicetype_list' %}?manufacturer={{ devicetype.manufacturer.slug }}">{{ devicetype.manufacturer }}</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Model Name</td>
|
|
||||||
<td>
|
|
||||||
{{ devicetype.model }}<br/>
|
|
||||||
<small class="text-muted">{{ devicetype.slug }}</small>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Part Number</td>
|
|
||||||
<td>{{ devicetype.part_number|placeholder }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Height (U)</td>
|
|
||||||
<td>{{ devicetype.u_height }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Full Depth</td>
|
|
||||||
<td>
|
|
||||||
{% if devicetype.is_full_depth %}
|
|
||||||
<i class="glyphicon glyphicon-ok text-success" title="Yes"></i>
|
|
||||||
{% else %}
|
|
||||||
<i class="glyphicon glyphicon-remove text-danger" title="No"></i>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Parent/Child</td>
|
|
||||||
<td>
|
|
||||||
{{ devicetype.get_subdevice_role_display|placeholder }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Front Image</td>
|
|
||||||
<td>
|
|
||||||
{% if devicetype.front_image %}
|
|
||||||
<a href="{{ devicetype.front_image.url }}">
|
|
||||||
<img src="{{ devicetype.front_image.url }}" alt="{{ devicetype.front_image.name }}" class="img-responsive" />
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">—</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Rear Image</td>
|
|
||||||
<td>
|
|
||||||
{% if devicetype.rear_image %}
|
|
||||||
<a href="{{ devicetype.rear_image.url }}">
|
|
||||||
<img src="{{ devicetype.rear_image.url }}" alt="{{ devicetype.rear_image.name }}" class="img-responsive" />
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">—</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Instances</td>
|
|
||||||
<td><a href="{% url 'dcim:device_list' %}?device_type_id={{ devicetype.pk }}">{{ instance_count }}</a></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
{% plugin_left_page devicetype %}
|
<div class="col-md-6">
|
||||||
</div>
|
{% include 'inc/custom_fields_panel.html' with obj=devicetype %}
|
||||||
<div class="col-md-6">
|
{% include 'extras/inc/tags_panel.html' with tags=devicetype.tags.all url='dcim:devicetype_list' %}
|
||||||
{% include 'inc/custom_fields_panel.html' with obj=devicetype %}
|
<div class="panel panel-default">
|
||||||
{% include 'extras/inc/tags_panel.html' with tags=devicetype.tags.all url='dcim:devicetype_list' %}
|
<div class="panel-heading">
|
||||||
<div class="panel panel-default">
|
<strong>Comments</strong>
|
||||||
<div class="panel-heading">
|
</div>
|
||||||
<strong>Comments</strong>
|
<div class="panel-body rendered-markdown">
|
||||||
|
{% if devicetype.comments %}
|
||||||
|
{{ devicetype.comments|render_markdown }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body rendered-markdown">
|
{% plugin_right_page devicetype %}
|
||||||
{% if devicetype.comments %}
|
</div>
|
||||||
{{ devicetype.comments|render_markdown }}
|
</div>
|
||||||
{% else %}
|
<div class="row">
|
||||||
<span class="text-muted">None</span>
|
<div class="col-md-12">
|
||||||
{% endif %}
|
{% plugin_full_width_page devicetype %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
|
<li role="presentation" class="active">
|
||||||
|
<a href="#interfaces" role="tab" data-toggle="tab">Interfaces {% badge interface_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#frontports" role="tab" data-toggle="tab">Front Ports {% badge front_port_table_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#rearports" role="tab" data-toggle="tab">Rear Ports {% badge rear_port_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#consoleports" role="tab" data-toggle="tab">Console Ports {% badge consoleport_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#consoleserverports" role="tab" data-toggle="tab">Console Server Ports {% badge consoleserverport_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#powerports" role="tab" data-toggle="tab">Power Ports {% badge powerport_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#poweroutlets" role="tab" data-toggle="tab">Power Outlets {% badge poweroutlet_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a href="#devicebays" role="tab" data-toggle="tab">Device Bays {% badge devicebay_table.rows|length %}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="interfaces">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="frontports">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=front_port_table title='Front Ports' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="rearports">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=rear_port_table title='Rear Ports' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="consoleports">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="consoleserverports">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="powerports">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="poweroutlets">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' %}
|
||||||
|
</div>
|
||||||
|
<div role="tabpanel" class="tab-pane" id="devicebays">
|
||||||
|
{% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% plugin_right_page devicetype %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% if devicetype.consoleport_templates.exists or devicetype.powerport_templates.exists %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' add_url='dcim:consoleporttemplate_add' edit_url='dcim:consoleporttemplate_bulk_edit' delete_url='dcim:consoleporttemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' add_url='dcim:powerporttemplate_add' edit_url='dcim:powerporttemplate_bulk_edit' delete_url='dcim:powerporttemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% plugin_full_width_page devicetype %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if devicetype.is_parent_device or devicebay_table.rows %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=devicebay_table title='Device Bays' add_url='dcim:devicebaytemplate_add' edit_url='dcim:devicebaytemplate_bulk_edit' delete_url='dcim:devicebaytemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if devicetype.consoleserverport_templates.exists %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' add_url='dcim:consoleserverporttemplate_add' edit_url='dcim:consoleserverporttemplate_bulk_edit' delete_url='dcim:consoleserverporttemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if devicetype.poweroutlet_templates.exists %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' add_url='dcim:poweroutlettemplate_add' edit_url='dcim:poweroutlettemplate_bulk_edit' delete_url='dcim:poweroutlettemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if devicetype.interface_templates.exists %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' add_url='dcim:interfacetemplate_add' edit_url='dcim:interfacetemplate_bulk_edit' delete_url='dcim:interfacetemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if devicetype.frontport_templates.exists or devicetype.rearport_templates.exists %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=front_port_table title='Front Ports' add_url='dcim:frontporttemplate_add' edit_url='dcim:frontporttemplate_bulk_edit' delete_url='dcim:frontporttemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
{% include 'dcim/inc/devicetype_component_table.html' with table=rear_port_table title='Rear Ports' add_url='dcim:rearporttemplate_add' edit_url='dcim:rearporttemplate_bulk_edit' delete_url='dcim:rearporttemplate_bulk_delete' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
{% if cp.type %}{{ cp.get_type_display }}{% else %}—{% endif %}
|
{% if cp.type %}{{ cp.get_type_display }}{% else %}—{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td></td>
|
|
||||||
|
|
||||||
{# Description #}
|
{# Description #}
|
||||||
<td>
|
<td>
|
||||||
{{ cp.description }}
|
{{ cp.description }}
|
||||||
|
40
netbox/templates/dcim/inc/device_component_table.html
Normal file
40
netbox/templates/dcim/inc/device_component_table.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% load helpers %}
|
||||||
|
{% load perms %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>{{ title }}</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover panel-body component-list">
|
||||||
|
{% for obj in components %}
|
||||||
|
{% include component_template %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
<div class="panel-footer noprint">
|
||||||
|
{% if components and perms.dcim.change_consoleport %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if components and perms.dcim.delete_consoleport %}
|
||||||
|
<button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if components and perms.dcim.add_consoleport %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -1,3 +1,4 @@
|
|||||||
|
{% load helpers %}
|
||||||
{% if perms.dcim.change_devicetype %}
|
{% if perms.dcim.change_devicetype %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -8,19 +9,18 @@
|
|||||||
{% include 'responsive_table.html' %}
|
{% include 'responsive_table.html' %}
|
||||||
<div class="panel-footer noprint">
|
<div class="panel-footer noprint">
|
||||||
{% if table.rows %}
|
{% if table.rows %}
|
||||||
{% if edit_url %}
|
<button type="submit" name="_edit" formaction="{% url table.Meta.model|viewname:"bulk_rename" %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
||||||
<button type="submit" name="_edit" formaction="{% url edit_url %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
|
||||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Selected
|
</button>
|
||||||
</button>
|
<button type="submit" name="_edit" formaction="{% url table.Meta.model|viewname:"bulk_edit" %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-warning">
|
||||||
{% endif %}
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
|
||||||
{% if delete_url %}
|
</button>
|
||||||
<button type="submit" name="_delete" formaction="{% url delete_url %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-danger">
|
<button type="submit" name="_delete" formaction="{% url table.Meta.model|viewname:"bulk_delete" %}?return_url={{ devicetype.get_absolute_url }}" class="btn btn-xs btn-danger">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="{% url add_url %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}" class="btn btn-primary btn-xs">
|
<a href="{% url table.Meta.model|viewname:"add" %}?device_type={{ devicetype.pk }}&return_url={{ devicetype.get_absolute_url }}" class="btn btn-primary btn-xs">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
Add {{ title }}
|
Add {{ title }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
{% if iface.description %}
|
{% if iface.description %}
|
||||||
{{ iface.description }}<br/>
|
{{ iface.description }}<br/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for tag in iface.tags.all %}
|
{% for tag in iface.tags.all.unrestricted %}
|
||||||
{% tag tag %}
|
{% tag tag %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
{% if not iface.description %}—{% endif %}
|
{% if not iface.description %}—{% endif %}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<strong>Tags</strong>
|
<strong>Tags</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% for tag in tags.all %}
|
{% for tag in tags.all.unrestricted %}
|
||||||
{% tag tag url %}
|
{% tag tag url %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<span class="text-muted">No tags assigned</span>
|
<span class="text-muted">No tags assigned</span>
|
||||||
|
1
netbox/templates/utilities/templatetags/badge.html
Normal file
1
netbox/templates/utilities/templatetags/badge.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% if value or show_empty %}<span class="badge">{{ value }}</span>{% endif %}
|
@ -130,25 +130,28 @@ class ButtonsColumn(tables.TemplateColumn):
|
|||||||
:param model: Model class to use for calculating URL view names
|
:param model: Model class to use for calculating URL view names
|
||||||
:param prepend_content: Additional template content to render in the column (optional)
|
:param prepend_content: Additional template content to render in the column (optional)
|
||||||
"""
|
"""
|
||||||
|
buttons = ('changelog', 'edit', 'delete')
|
||||||
attrs = {'td': {'class': 'text-right text-nowrap noprint'}}
|
attrs = {'td': {'class': 'text-right text-nowrap noprint'}}
|
||||||
# Note that braces are escaped to allow for string formatting prior to template rendering
|
# Note that braces are escaped to allow for string formatting prior to template rendering
|
||||||
template_code = """
|
template_code = """
|
||||||
<a href="{{% url '{app_label}:{model_name}_changelog' {pk_field}=record.{pk_field} %}}" class="btn btn-default btn-xs" title="Change log">
|
{{% if "changelog" in buttons %}}
|
||||||
<i class="fa fa-history"></i>
|
<a href="{{% url '{app_label}:{model_name}_changelog' {pk_field}=record.{pk_field} %}}" class="btn btn-default btn-xs" title="Change log">
|
||||||
</a>
|
<i class="fa fa-history"></i>
|
||||||
{{% if perms.{app_label}.change_{model_name} %}}
|
</a>
|
||||||
|
{{% endif %}}
|
||||||
|
{{% if "edit" in buttons and perms.{app_label}.change_{model_name} %}}
|
||||||
<a href="{{% url '{app_label}:{model_name}_edit' {pk_field}=record.{pk_field} %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-warning" title="Edit">
|
<a href="{{% url '{app_label}:{model_name}_edit' {pk_field}=record.{pk_field} %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-warning" title="Edit">
|
||||||
<i class="fa fa-pencil"></i>
|
<i class="fa fa-pencil"></i>
|
||||||
</a>
|
</a>
|
||||||
{{% endif %}}
|
{{% endif %}}
|
||||||
{{% if perms.{app_label}.delete_{model_name} %}}
|
{{% if "delete" in buttons and perms.{app_label}.delete_{model_name} %}}
|
||||||
<a href="{{% url '{app_label}:{model_name}_delete' {pk_field}=record.{pk_field} %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-danger" title="Delete">
|
<a href="{{% url '{app_label}:{model_name}_delete' {pk_field}=record.{pk_field} %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-danger" title="Delete">
|
||||||
<i class="fa fa-trash"></i>
|
<i class="fa fa-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
{{% endif %}}
|
{{% endif %}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, model, *args, pk_field='pk', prepend_template=None, **kwargs):
|
def __init__(self, model, *args, pk_field='pk', buttons=None, prepend_template=None, **kwargs):
|
||||||
if prepend_template:
|
if prepend_template:
|
||||||
prepend_template = prepend_template.replace('{', '{{')
|
prepend_template = prepend_template.replace('{', '{{')
|
||||||
prepend_template = prepend_template.replace('}', '}}')
|
prepend_template = prepend_template.replace('}', '}}')
|
||||||
@ -157,11 +160,16 @@ class ButtonsColumn(tables.TemplateColumn):
|
|||||||
template_code = self.template_code.format(
|
template_code = self.template_code.format(
|
||||||
app_label=model._meta.app_label,
|
app_label=model._meta.app_label,
|
||||||
model_name=model._meta.model_name,
|
model_name=model._meta.model_name,
|
||||||
pk_field=pk_field
|
pk_field=pk_field,
|
||||||
|
buttons=buttons
|
||||||
)
|
)
|
||||||
|
|
||||||
super().__init__(template_code=template_code, *args, **kwargs)
|
super().__init__(template_code=template_code, *args, **kwargs)
|
||||||
|
|
||||||
|
self.extra_context.update({
|
||||||
|
'buttons': buttons or self.buttons,
|
||||||
|
})
|
||||||
|
|
||||||
def header(self):
|
def header(self):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -242,3 +242,14 @@ def tag(tag, url_name=None):
|
|||||||
'tag': tag,
|
'tag': tag,
|
||||||
'url_name': url_name,
|
'url_name': url_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@register.inclusion_tag('utilities/templatetags/badge.html')
|
||||||
|
def badge(value, show_empty=False):
|
||||||
|
"""
|
||||||
|
Display the specified number as a badge.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'value': value,
|
||||||
|
'show_empty': show_empty,
|
||||||
|
}
|
||||||
|
@ -217,7 +217,7 @@ def prepare_cloned_fields(instance):
|
|||||||
|
|
||||||
# Copy tags
|
# Copy tags
|
||||||
if is_taggable(instance):
|
if is_taggable(instance):
|
||||||
params['tags'] = ','.join([t.name for t in instance.tags.all()])
|
params['tags'] = ','.join([t.name for t in instance.tags.all().unrestricted()])
|
||||||
|
|
||||||
# Concatenate parameters into a URL query string
|
# Concatenate parameters into a URL query string
|
||||||
param_string = '&'.join(
|
param_string = '&'.join(
|
||||||
|
@ -51,8 +51,8 @@ def replicate_interfaces(apps, schema_editor):
|
|||||||
# Verify that all interfaces have been replicated
|
# Verify that all interfaces have been replicated
|
||||||
assert replicated_count == original_interfaces.count(), "Replicated interfaces count does not match original count!"
|
assert replicated_count == original_interfaces.count(), "Replicated interfaces count does not match original count!"
|
||||||
|
|
||||||
# Delete original VM interfaces
|
# Delete all interfaces not assigned to a Device
|
||||||
original_interfaces.delete()
|
Interface.objects.filter(device__isnull=True).delete()
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -9,7 +9,9 @@ from dcim.choices import InterfaceModeChoices
|
|||||||
from dcim.models import BaseInterface, Device
|
from dcim.models import BaseInterface, Device
|
||||||
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
|
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
|
||||||
from extras.utils import extras_features
|
from extras.utils import extras_features
|
||||||
|
from utilities.fields import NaturalOrderingField
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
|
from utilities.ordering import naturalize_interface
|
||||||
from utilities.query_functions import CollateAsChar
|
from utilities.query_functions import CollateAsChar
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
from utilities.utils import serialize_object
|
from utilities.utils import serialize_object
|
||||||
@ -387,6 +389,15 @@ class VMInterface(BaseInterface):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='interfaces'
|
related_name='interfaces'
|
||||||
)
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=64
|
||||||
|
)
|
||||||
|
_name = NaturalOrderingField(
|
||||||
|
target_field='name',
|
||||||
|
naturalize_function=naturalize_interface,
|
||||||
|
max_length=100,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True
|
blank=True
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.test import override_settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
@ -244,7 +245,8 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_get_interface_graphs(self):
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_get_vminterface_graphs(self):
|
||||||
"""
|
"""
|
||||||
Test retrieval of Graphs assigned to VM interfaces.
|
Test retrieval of Graphs assigned to VM interfaces.
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user