mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-01 21:36:25 -06:00
Changes for JIRA-3333
This commit is contained in:
parent
4ab3854d66
commit
80943c7ab9
@ -2,7 +2,7 @@ from rest_framework import serializers
|
||||
|
||||
from dcim.constants import CONNECTION_STATUS_CHOICES
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceType, DeviceRole, FrontPort, FrontPortTemplate,
|
||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceType, InventoryItemType, DeviceRole, FrontPort, FrontPortTemplate,
|
||||
Interface, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerPanel, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
@ -16,6 +16,7 @@ __all__ = [
|
||||
'NestedDeviceRoleSerializer',
|
||||
'NestedDeviceSerializer',
|
||||
'NestedDeviceTypeSerializer',
|
||||
'NestedInventoryItemTypeSerializer',
|
||||
'NestedFrontPortSerializer',
|
||||
'NestedFrontPortTemplateSerializer',
|
||||
'NestedInterfaceSerializer',
|
||||
@ -112,6 +113,16 @@ class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
||||
fields = ['id', 'url', 'manufacturer', 'model', 'slug', 'display_name', 'device_count']
|
||||
|
||||
|
||||
class NestedInventoryItemTypeSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemtype-detail')
|
||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||
instance_count = serializers.IntegerField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemType
|
||||
fields = ['id', 'url', 'manufacturer', 'model', 'slug', 'instance_count']
|
||||
|
||||
|
||||
class NestedPowerPortTemplateSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
|
||||
|
||||
|
@ -9,7 +9,7 @@ from dcim.constants import *
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
Manufacturer, InventoryItem, InventoryItemRole, InventoryItemType, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
@ -332,10 +332,41 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||
fields = ['id', 'device_type', 'name']
|
||||
|
||||
|
||||
#
|
||||
# Inventory Item Role
|
||||
#
|
||||
|
||||
class InventoryItemRoleSerializer(ValidatedModelSerializer):
|
||||
inventoryitem_count = serializers.IntegerField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemRole
|
||||
fields = [
|
||||
'id', 'name', 'slug', 'inventoryitem_count'
|
||||
]
|
||||
|
||||
#
|
||||
# Inventory Item Type
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
manufacturer = NestedManufacturerSerializer()
|
||||
instance_count = serializers.IntegerField(read_only=True)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemType
|
||||
fields = [
|
||||
'id', 'manufacturer', 'model', 'slug', 'part_number', 'tags', 'created',
|
||||
'last_updated', 'instance_count',
|
||||
]
|
||||
|
||||
#
|
||||
# Devices
|
||||
#
|
||||
|
||||
|
||||
class DeviceRoleSerializer(ValidatedModelSerializer):
|
||||
device_count = serializers.IntegerField(read_only=True)
|
||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
||||
@ -612,14 +643,13 @@ class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
# Provide a default value to satisfy UniqueTogetherValidator
|
||||
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
||||
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItem
|
||||
fields = [
|
||||
'id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
|
||||
'description', 'tags',
|
||||
'id', 'device', 'parent', 'name', 'part_id', 'serial', 'asset_tag', 'discovered',
|
||||
'description', 'tags', 'role', 'type', 'site',
|
||||
]
|
||||
|
||||
|
||||
|
@ -65,6 +65,12 @@ router.register('interface-connections', views.InterfaceConnectionViewSet, basen
|
||||
# Cables
|
||||
router.register('cables', views.CableViewSet)
|
||||
|
||||
# Inventory Item Roles
|
||||
router.register('inventory-item-roles', views.InventoryItemRoleViewSet)
|
||||
|
||||
# Inventory Item types
|
||||
router.register('inventory-item-types', views.InventoryItemTypeViewSet)
|
||||
|
||||
# Virtual chassis
|
||||
router.register('virtual-chassis', views.VirtualChassisViewSet)
|
||||
|
||||
|
@ -17,7 +17,7 @@ from dcim import filters
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
Manufacturer, InventoryItem, InventoryItemRole, InventoryItemType, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
@ -269,7 +269,7 @@ class RackReservationViewSet(ModelViewSet):
|
||||
class ManufacturerViewSet(ModelViewSet):
|
||||
queryset = Manufacturer.objects.annotate(
|
||||
devicetype_count=get_subquery(DeviceType, 'manufacturer'),
|
||||
inventoryitem_count=get_subquery(InventoryItem, 'manufacturer'),
|
||||
inventoryitem_count=get_subquery(InventoryItem, 'type__manufacturer'),
|
||||
platform_count=get_subquery(Platform, 'manufacturer')
|
||||
)
|
||||
serializer_class = serializers.ManufacturerSerializer
|
||||
@ -352,6 +352,32 @@ class DeviceRoleViewSet(ModelViewSet):
|
||||
serializer_class = serializers.DeviceRoleSerializer
|
||||
filterset_class = filters.DeviceRoleFilterSet
|
||||
|
||||
#
|
||||
# Inventory item roles
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemRoleViewSet(ModelViewSet):
|
||||
|
||||
queryset = InventoryItemRole.objects.annotate(
|
||||
inventoryitem_count=get_subquery(InventoryItem, 'role'),
|
||||
)
|
||||
|
||||
serializer_class = serializers.InventoryItemRoleSerializer
|
||||
filterset_class = filters.InventoryItemRoleFilterSet
|
||||
|
||||
#
|
||||
# Inventory Item types
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemTypeViewSet(ModelViewSet):
|
||||
queryset = InventoryItemType.objects.prefetch_related('manufacturer').prefetch_related('tags').annotate(
|
||||
instance_count=Count('instances')
|
||||
)
|
||||
serializer_class = serializers.InventoryItemTypeSerializer
|
||||
filterset_class = filters.InventoryItemTypeFilterSet
|
||||
|
||||
|
||||
#
|
||||
# Platforms
|
||||
@ -576,7 +602,7 @@ class DeviceBayViewSet(ModelViewSet):
|
||||
|
||||
|
||||
class InventoryItemViewSet(ModelViewSet):
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer').prefetch_related('tags')
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'type__manufacturer').prefetch_related('tags')
|
||||
serializer_class = serializers.InventoryItemSerializer
|
||||
filterset_class = filters.InventoryItemFilterSet
|
||||
|
||||
|
@ -15,9 +15,9 @@ from .constants import *
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
InventoryItem, InventoryItemRole, InventoryItemType, Manufacturer, Platform, PowerFeed, PowerOutlet,
|
||||
PowerOutletTemplate, PowerPanel, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort,
|
||||
RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
|
||||
|
||||
@ -39,6 +39,8 @@ __all__ = (
|
||||
'InterfaceFilterSet',
|
||||
'InterfaceTemplateFilterSet',
|
||||
'InventoryItemFilterSet',
|
||||
'InventoryItemRoleFilterSet',
|
||||
'InventoryItemTypeFilterSet',
|
||||
'ManufacturerFilterSet',
|
||||
'PlatformFilterSet',
|
||||
'PowerConnectionFilterSet',
|
||||
@ -479,6 +481,13 @@ class DeviceRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||
fields = ['id', 'name', 'slug', 'color', 'vm_role']
|
||||
|
||||
|
||||
class InventoryItemRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemRole
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class PlatformFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='manufacturer',
|
||||
@ -989,15 +998,25 @@ class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
||||
queryset=InventoryItem.objects.all(),
|
||||
label='Parent inventory item (ID)',
|
||||
)
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
label='Manufacturer (ID)',
|
||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=InventoryItemRole.objects.all(),
|
||||
label='Inventory Item Role (ID)'
|
||||
)
|
||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='manufacturer__slug',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
role = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='role__slug',
|
||||
queryset=InventoryItemRole.objects.all(),
|
||||
to_field_name="slug",
|
||||
label="Inventory Item Role (slug)"
|
||||
)
|
||||
type_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=InventoryItemType.objects.all(),
|
||||
label='Inventory Item Type (ID)'
|
||||
)
|
||||
type = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='type__slug',
|
||||
queryset=InventoryItemType.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Manufacturer (slug)',
|
||||
label='Inventory Item Type (slug)'
|
||||
)
|
||||
serial = django_filters.CharFilter(
|
||||
lookup_expr='iexact'
|
||||
@ -1020,6 +1039,44 @@ class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
||||
return queryset.filter(qs_filter)
|
||||
|
||||
|
||||
class InventoryItemTypeFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
|
||||
id__in = NumericInFilter(
|
||||
field_name='id',
|
||||
lookup_expr='in'
|
||||
)
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
label='Manufacturer (ID)',
|
||||
)
|
||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||
field_name='manufacturer__slug',
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='slug',
|
||||
label='Manufacturer (slug)',
|
||||
)
|
||||
tag = TagFilter()
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemType
|
||||
fields = [
|
||||
'model', 'slug', 'part_number'
|
||||
]
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
return queryset
|
||||
return queryset.filter(
|
||||
Q(manufacturer__name__icontains=value) |
|
||||
Q(model__icontains=value) |
|
||||
Q(part_number__icontains=value) |
|
||||
Q(slug__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class VirtualChassisFilterSet(BaseFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
|
@ -32,8 +32,9 @@ from .constants import *
|
||||
from .models import (
|
||||
Cable, DeviceBay, DeviceBayTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate,
|
||||
Device, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, Manufacturer,
|
||||
InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort, PowerPortTemplate,
|
||||
Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
InventoryItem, InventoryItemRole, InventoryItemType, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate,
|
||||
PowerPanel, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate,
|
||||
Region, Site, VirtualChassis,
|
||||
)
|
||||
|
||||
DEVICE_BY_PK_RE = r'{\d+\}'
|
||||
@ -58,7 +59,6 @@ def get_device_by_name_or_pk(name):
|
||||
|
||||
|
||||
class DeviceComponentFilterForm(BootstrapMixin, forms.Form):
|
||||
|
||||
field_order = [
|
||||
'q', 'region', 'site'
|
||||
]
|
||||
@ -923,7 +923,6 @@ class ManufacturerForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
|
||||
class ManufacturerCSVForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Manufacturer
|
||||
fields = Manufacturer.csv_headers
|
||||
@ -1065,7 +1064,6 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
#
|
||||
|
||||
class ConsolePortTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = ConsolePortTemplate
|
||||
fields = [
|
||||
@ -1105,7 +1103,6 @@ class ConsolePortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPortTemplate
|
||||
fields = [
|
||||
@ -1145,7 +1142,6 @@ class ConsoleServerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = PowerPortTemplate
|
||||
fields = [
|
||||
@ -1205,7 +1201,6 @@ class PowerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = PowerOutletTemplate
|
||||
fields = [
|
||||
@ -1216,7 +1211,6 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit power_port choices to current DeviceType
|
||||
@ -1280,7 +1274,6 @@ class PowerOutletTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = InterfaceTemplate
|
||||
fields = [
|
||||
@ -1330,7 +1323,6 @@ class InterfaceTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = FrontPortTemplate
|
||||
fields = [
|
||||
@ -1342,7 +1334,6 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Limit rear_port choices to current DeviceType
|
||||
@ -1431,7 +1422,6 @@ class FrontPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class RearPortTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = RearPortTemplate
|
||||
fields = [
|
||||
@ -1478,7 +1468,6 @@ class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
|
||||
|
||||
class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = DeviceBayTemplate
|
||||
fields = [
|
||||
@ -1537,7 +1526,6 @@ class ComponentTemplateImportForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
|
||||
class ConsolePortTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
class Meta:
|
||||
model = ConsolePortTemplate
|
||||
fields = [
|
||||
@ -1546,7 +1534,6 @@ class ConsolePortTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
|
||||
class ConsoleServerPortTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPortTemplate
|
||||
fields = [
|
||||
@ -1555,7 +1542,6 @@ class ConsoleServerPortTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
|
||||
class PowerPortTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
class Meta:
|
||||
model = PowerPortTemplate
|
||||
fields = [
|
||||
@ -1619,7 +1605,6 @@ class RearPortTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
|
||||
class DeviceBayTemplateImportForm(ComponentTemplateImportForm):
|
||||
|
||||
class Meta:
|
||||
model = DeviceBayTemplate
|
||||
fields = [
|
||||
@ -1814,7 +1799,8 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||
|
||||
if 'device_type' in kwargs['initial'] and 'manufacturer' not in kwargs['initial']:
|
||||
device_type_id = kwargs['initial']['device_type']
|
||||
manufacturer_id = DeviceType.objects.filter(pk=device_type_id).values_list('manufacturer__pk', flat=True).first()
|
||||
manufacturer_id = DeviceType.objects.filter(pk=device_type_id).values_list('manufacturer__pk',
|
||||
flat=True).first()
|
||||
kwargs['initial']['manufacturer'] = manufacturer_id
|
||||
|
||||
if 'cluster' in kwargs['initial'] and 'cluster_group' not in kwargs['initial']:
|
||||
@ -3671,7 +3657,6 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
|
||||
class CableForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Cable
|
||||
fields = [
|
||||
@ -3685,7 +3670,6 @@ class CableForm(BootstrapMixin, forms.ModelForm):
|
||||
|
||||
|
||||
class CableCSVForm(forms.ModelForm):
|
||||
|
||||
# Termination A
|
||||
side_a_device = FlexibleModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
@ -3853,7 +3837,6 @@ class CableBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate length/unit
|
||||
length = self.cleaned_data.get('length')
|
||||
length_unit = self.cleaned_data.get('length_unit')
|
||||
@ -3970,7 +3953,6 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
|
||||
def __init__(self, device_bay, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['installed_device'].queryset = Device.objects.filter(
|
||||
@ -4187,6 +4169,30 @@ class InventoryItemCSVForm(forms.ModelForm):
|
||||
'invalid_choice': 'Invalid manufacturer.',
|
||||
}
|
||||
)
|
||||
site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False,
|
||||
error_messages={
|
||||
"invalid_choice": 'Invalid site.',
|
||||
}
|
||||
)
|
||||
role = forms.ModelChoiceField(
|
||||
queryset=InventoryItemRole.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False,
|
||||
error_messages={
|
||||
'invalid_choice': 'Invalid item role.',
|
||||
}
|
||||
)
|
||||
type = forms.ModelChoiceField(
|
||||
queryset=InventoryItemType.objects.all(),
|
||||
to_field_name='model',
|
||||
required=False,
|
||||
error_messages={
|
||||
'invalid_choice': 'Invalie item type.',
|
||||
}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItem
|
||||
@ -4202,10 +4208,6 @@ class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
queryset=Device.objects.all(),
|
||||
required=False
|
||||
)
|
||||
manufacturer = DynamicModelChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
required=False
|
||||
)
|
||||
part_id = forms.CharField(
|
||||
max_length=50,
|
||||
required=False,
|
||||
@ -4215,10 +4217,25 @@ class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||
max_length=100,
|
||||
required=False
|
||||
)
|
||||
site = DynamicModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url="/api/dcim/sites"
|
||||
)
|
||||
)
|
||||
role = DynamicModelChoiceField(
|
||||
queryset=InventoryItemRole.objects.all(),
|
||||
required=False
|
||||
)
|
||||
type = DynamicModelChoiceField(
|
||||
queryset=InventoryItemType.objects.all(),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = [
|
||||
'manufacturer', 'part_id', 'description',
|
||||
'part_id', 'description', 'site', 'role', 'type',
|
||||
]
|
||||
|
||||
|
||||
@ -4263,6 +4280,16 @@ class InventoryItemFilterForm(BootstrapMixin, forms.Form):
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
|
||||
role = DynamicModelMultipleChoiceField(
|
||||
queryset=InventoryItemRole.objects.all(),
|
||||
to_field_name='slug',
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url="/api/dcim/inventory-item-roles/",
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
discovered = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=StaticSelect2(
|
||||
@ -4271,11 +4298,109 @@ class InventoryItemFilterForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
#
|
||||
# Inventory Item types
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemTypeForm(BootstrapMixin, CustomFieldModelForm):
|
||||
manufacturer = DynamicModelChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
widget=APISelect(
|
||||
api_url="/api/dcim/manufacturers/",
|
||||
)
|
||||
)
|
||||
slug = SlugField(
|
||||
slug_source='model'
|
||||
)
|
||||
tags = TagField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemType
|
||||
fields = [
|
||||
'manufacturer', 'model', 'slug', 'part_number', 'tags',
|
||||
]
|
||||
|
||||
|
||||
class InventoryItemTypeImportForm(BootstrapMixin, forms.ModelForm):
|
||||
manufacturer = forms.ModelChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='name'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemType
|
||||
fields = [
|
||||
'manufacturer', 'model', 'slug', 'part_number',
|
||||
]
|
||||
|
||||
|
||||
class InventoryItemTypeBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=InventoryItemType.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
manufacturer = DynamicModelChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url="/api/dcim/manufacturers"
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = []
|
||||
|
||||
|
||||
class InventoryItemTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
model = InventoryItemType
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search'
|
||||
)
|
||||
manufacturer = DynamicModelMultipleChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
to_field_name='slug',
|
||||
required=False,
|
||||
widget=APISelectMultiple(
|
||||
api_url="/api/dcim/manufacturers/",
|
||||
value_field="slug",
|
||||
)
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
#
|
||||
# Inventory Item Role
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemRoleCSVForm(forms.ModelForm):
|
||||
slug = SlugField()
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemRole
|
||||
fields = InventoryItemRole.csv_headers
|
||||
help_texts = {
|
||||
'name': 'Name of inventory item role',
|
||||
}
|
||||
|
||||
|
||||
class InventoryItemRoleForm(BootstrapMixin, forms.ModelForm):
|
||||
slug = SlugField()
|
||||
|
||||
class Meta:
|
||||
model = InventoryItemRole
|
||||
fields = [
|
||||
'name', 'slug',
|
||||
]
|
||||
|
||||
#
|
||||
# Virtual chassis
|
||||
#
|
||||
|
||||
|
||||
class DeviceSelectionForm(forms.Form):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
|
66
netbox/dcim/migrations/0100_auto_20200327_0158.py
Normal file
66
netbox/dcim/migrations/0100_auto_20200327_0158.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Generated by Django 2.2.10 on 2020-03-27 01:58
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0099_powerfeed_negative_voltage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InventoryItemRole',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('name', models.CharField(max_length=50, unique=True)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='inventoryitem',
|
||||
name='manufacturer',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inventoryitem',
|
||||
name='site',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.Site'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='device',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.Device'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InventoryItemType',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('model', models.CharField(max_length=50)),
|
||||
('part_number', models.CharField(blank=True, max_length=50)),
|
||||
('slug', models.SlugField(unique=True)),
|
||||
('manufacturer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='item_types', to='dcim.Manufacturer')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['model'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inventoryitem',
|
||||
name='role',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.InventoryItemRole'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inventoryitem',
|
||||
name='type',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='items', to='dcim.InventoryItemType'),
|
||||
),
|
||||
]
|
44
netbox/dcim/migrations/0101_auto_20200331_1943.py
Normal file
44
netbox/dcim/migrations/0101_auto_20200331_1943.py
Normal file
@ -0,0 +1,44 @@
|
||||
# Generated by Django 2.2.10 on 2020-03-31 19:43
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0038_webhook_template_support'),
|
||||
('dcim', '0100_auto_20200327_0158'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='inventoryitemtype',
|
||||
options={'ordering': ['manufacturer', 'model']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inventoryitemtype',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='type',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.InventoryItemType'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitemtype',
|
||||
name='manufacturer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_types', to='dcim.Manufacturer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitemtype',
|
||||
name='slug',
|
||||
field=models.SlugField(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='inventoryitemtype',
|
||||
unique_together={('manufacturer', 'slug'), ('manufacturer', 'model')},
|
||||
),
|
||||
]
|
19
netbox/dcim/migrations/0102_auto_20200401_1850.py
Normal file
19
netbox/dcim/migrations/0102_auto_20200401_1850.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.2.11 on 2020-04-01 18:50
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0101_auto_20200331_1943'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='inventoryitem',
|
||||
name='role',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventoryitems', to='dcim.InventoryItemRole'),
|
||||
),
|
||||
]
|
@ -52,6 +52,8 @@ __all__ = (
|
||||
'Interface',
|
||||
'InterfaceTemplate',
|
||||
'InventoryItem',
|
||||
'InventoryItemRole',
|
||||
'InventoryItemType',
|
||||
'Manufacturer',
|
||||
'Platform',
|
||||
'PowerFeed',
|
||||
@ -2179,3 +2181,91 @@ class Cable(ChangeLoggedModel):
|
||||
b_endpoint = b_path[-1][2]
|
||||
|
||||
return a_endpoint, b_endpoint, path_status
|
||||
|
||||
#
|
||||
# Inventory Item Role
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemRole(ChangeLoggedModel):
|
||||
"""
|
||||
Inventory Item Role
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=50,
|
||||
unique=True
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True
|
||||
)
|
||||
|
||||
csv_headers = ['name', 'slug']
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.name,
|
||||
self.slug,
|
||||
)
|
||||
|
||||
|
||||
class InventoryItemType(ChangeLoggedModel):
|
||||
manufacturer = models.ForeignKey(
|
||||
to='dcim.Manufacturer',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='inventory_item_types'
|
||||
)
|
||||
model = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
slug = models.SlugField()
|
||||
part_number = models.CharField(
|
||||
max_length=50,
|
||||
blank=True,
|
||||
help_text='Discrete part number (optional)'
|
||||
)
|
||||
|
||||
tags = TaggableManager(through=TaggedItem)
|
||||
|
||||
clone_fields = [
|
||||
'manufacturer'
|
||||
]
|
||||
|
||||
class Meta:
|
||||
ordering = ['manufacturer', 'model']
|
||||
unique_together = [
|
||||
['manufacturer', 'model'],
|
||||
['manufacturer', 'slug'],
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.model
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:inventoryitemtype', args=[self.pk])
|
||||
|
||||
def to_yaml(self):
|
||||
data = OrderedDict((
|
||||
('manufacturer', self.manufacturer.name),
|
||||
('model', self.model),
|
||||
('slug', self.slug),
|
||||
('part_number', self.part_number),
|
||||
))
|
||||
|
||||
return yaml.dump(dict(data), sort_keys=False)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
ret = super().save(*args, **kwargs)
|
||||
|
||||
return ret
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
|
@ -1027,8 +1027,28 @@ class InventoryItem(ComponentModel):
|
||||
"""
|
||||
device = models.ForeignKey(
|
||||
to='dcim.Device',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='inventory_items'
|
||||
on_delete=models.SET_NULL,
|
||||
null=True
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
role = models.ForeignKey(
|
||||
to='dcim.InventoryItemRole',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='inventoryitems',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
type = models.ForeignKey(
|
||||
to='dcim.InventoryItemType',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='instances',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
parent = models.ForeignKey(
|
||||
to='self',
|
||||
@ -1046,13 +1066,6 @@ class InventoryItem(ComponentModel):
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
manufacturer = models.ForeignKey(
|
||||
to='dcim.Manufacturer',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='inventory_items',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
part_id = models.CharField(
|
||||
max_length=50,
|
||||
verbose_name='Part ID',
|
||||
@ -1079,7 +1092,7 @@ class InventoryItem(ComponentModel):
|
||||
tags = TaggableManager(through=TaggedItem)
|
||||
|
||||
csv_headers = [
|
||||
'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
||||
'device', 'name', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
@ -1090,13 +1103,15 @@ class InventoryItem(ComponentModel):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:device_inventory', kwargs={'pk': self.device.pk})
|
||||
return reverse('dcim:inventoryitem_list')
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.device.name or '{{{}}}'.format(self.device.pk),
|
||||
self.name,
|
||||
self.manufacturer.name if self.manufacturer else None,
|
||||
self.site,
|
||||
self.role,
|
||||
self.type,
|
||||
self.part_id,
|
||||
self.serial,
|
||||
self.asset_tag,
|
||||
|
@ -6,7 +6,7 @@ from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
InventoryItem, InventoryItemRole, InventoryItemType, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
@ -112,6 +112,20 @@ DEVICEROLE_ACTIONS = """
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
INVENTORYITEMROLE_ACTIONS = """
|
||||
<a href="{% url 'dcim:inventoryitemrole_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Change log">
|
||||
<i class="fa fa-history"></i>
|
||||
</a>
|
||||
|
||||
{% if perms.dcim.change_inventoryitemrole %}
|
||||
<a href="{% url 'dcim:inventoryitemrole_edit' slug=record.slug %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
INVENTORYITEMROLE_INVENTORYITEM_COUNT = """
|
||||
<a href="{% url 'dcim:inventoryitem_list' %}?role={{ record.slug }}">{{ value }}</a>
|
||||
"""
|
||||
|
||||
DEVICEROLE_DEVICE_COUNT = """
|
||||
<a href="{% url 'dcim:device_list' %}?role={{ record.slug }}">{{ value }}</a>
|
||||
"""
|
||||
@ -160,6 +174,10 @@ DEVICETYPE_INSTANCES_TEMPLATE = """
|
||||
<a href="{% url 'dcim:device_list' %}?manufacturer_id={{ record.manufacturer_id }}&device_type_id={{ record.pk }}">{{ record.instance_count }}</a>
|
||||
"""
|
||||
|
||||
INVENTORYITEMTYPE_INSTANCES_TEMPLATE = """
|
||||
<a href="{% url 'dcim:inventoryitem_list' %}?manufacturer_id={{ record.manufacturer_id }}&inventory_item_type_id={{ record.pk }}">{{ record.instance_count }}</a>
|
||||
"""
|
||||
|
||||
UTILIZATION_GRAPH = """
|
||||
{% load helpers %}
|
||||
{% utilization_graph value %}
|
||||
@ -1031,16 +1049,65 @@ class InventoryItemTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
device = tables.LinkColumn('dcim:device_inventory', args=[Accessor('device.pk')])
|
||||
manufacturer = tables.Column(accessor=Accessor('manufacturer.name'), verbose_name='Manufacturer')
|
||||
role = tables.Column(accessor=Accessor('role.name'), verbose_name='Role')
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = InventoryItem
|
||||
fields = ('pk', 'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description')
|
||||
|
||||
#
|
||||
# InventoryItemRoles
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemRoleTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
slug = tables.Column(verbose_name='Slug')
|
||||
inventoryitem_count = tables.TemplateColumn(
|
||||
template_code=INVENTORYITEMROLE_INVENTORYITEM_COUNT,
|
||||
accessor=Accessor('inventoryitems.count'),
|
||||
orderable=False,
|
||||
verbose_name='Inventory Items'
|
||||
)
|
||||
actions = tables.TemplateColumn(
|
||||
template_code=INVENTORYITEMROLE_ACTIONS,
|
||||
attrs={'td': {'class': 'text-right noprint'}},
|
||||
verbose_name=''
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = InventoryItemRole
|
||||
fields = ('pk', 'name', 'slug', 'inventoryitem_count')
|
||||
|
||||
#
|
||||
# Inventory Item types
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemTypeTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
model = tables.LinkColumn(
|
||||
viewname='dcim:inventoryitemtype',
|
||||
args=[Accessor('pk')],
|
||||
verbose_name='Inventory Item Type'
|
||||
)
|
||||
instance_count = tables.TemplateColumn(
|
||||
template_code=INVENTORYITEMTYPE_INSTANCES_TEMPLATE,
|
||||
verbose_name='Instances'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = InventoryItemType
|
||||
fields = (
|
||||
'pk', 'model', 'manufacturer', 'part_number', 'slug',
|
||||
'instance_count',
|
||||
)
|
||||
|
||||
#
|
||||
# Virtual chassis
|
||||
#
|
||||
|
||||
|
||||
class VirtualChassisTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
master = tables.LinkColumn()
|
||||
|
@ -6,13 +6,13 @@ from rest_framework import status
|
||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||
from dcim.api import serializers
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, Interface, InterfaceTemplate, Manufacturer,
|
||||
InventoryItem, Platform, PowerFeed, PowerPort, PowerPortTemplate, PowerOutlet, PowerOutletTemplate, PowerPanel,
|
||||
Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
|
||||
InventoryItem, InventoryItemRole, InventoryItemType, Platform, PowerFeed, PowerPort, PowerPortTemplate, PowerOutlet,
|
||||
PowerOutletTemplate, PowerPanel, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
|
||||
)
|
||||
from dcim.constants import *
|
||||
from ipam.models import IPAddress, VLAN
|
||||
from extras.models import Graph
|
||||
from utilities.testing import APITestCase, choices_to_dict
|
||||
@ -105,7 +105,6 @@ class AppTest(APITestCase):
|
||||
class RegionTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super().setUp()
|
||||
|
||||
self.region1 = Region.objects.create(name='Test Region 1', slug='test-region-1')
|
||||
@ -113,7 +112,6 @@ class RegionTest(APITestCase):
|
||||
self.region3 = Region.objects.create(name='Test Region 3', slug='test-region-3')
|
||||
|
||||
def test_get_region(self):
|
||||
|
||||
url = reverse('dcim-api:region-detail', kwargs={'pk': self.region1.pk})
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
@ -3249,7 +3247,7 @@ class InventoryItemTest(APITestCase):
|
||||
|
||||
super().setUp()
|
||||
|
||||
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||
self.site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||
self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||
devicetype = DeviceType.objects.create(
|
||||
manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
|
||||
@ -3258,8 +3256,16 @@ class InventoryItemTest(APITestCase):
|
||||
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
|
||||
)
|
||||
self.device = Device.objects.create(
|
||||
device_type=devicetype, device_role=devicerole, name='Test Device 1', site=site
|
||||
device_type=devicetype, device_role=devicerole, name='Test Device 1', site=self.site
|
||||
)
|
||||
|
||||
self.inventoryitemrole = InventoryItemRole.objects.create(
|
||||
name='Inventory Item Role 1', slug='inventory-item-role-1'
|
||||
)
|
||||
self.inventoryitemtype = InventoryItemType.objects.create(
|
||||
model='Inventory Item 1', manufacturer=self.manufacturer, slug='inventory-item-type-1'
|
||||
)
|
||||
|
||||
self.inventoryitem1 = InventoryItem.objects.create(device=self.device, name='Test Inventory Item 1')
|
||||
self.inventoryitem2 = InventoryItem.objects.create(device=self.device, name='Test Inventory Item 2')
|
||||
self.inventoryitem3 = InventoryItem.objects.create(device=self.device, name='Test Inventory Item 3')
|
||||
@ -3282,9 +3288,11 @@ class InventoryItemTest(APITestCase):
|
||||
|
||||
data = {
|
||||
'device': self.device.pk,
|
||||
'site': self.site.pk,
|
||||
'type': self.inventoryitemtype.pk,
|
||||
'role': self.inventoryitemrole.pk,
|
||||
'parent': self.inventoryitem1.pk,
|
||||
'name': 'Test Inventory Item 4',
|
||||
'manufacturer': self.manufacturer.pk,
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:inventoryitem-list')
|
||||
@ -3296,28 +3304,36 @@ class InventoryItemTest(APITestCase):
|
||||
self.assertEqual(inventoryitem4.device_id, data['device'])
|
||||
self.assertEqual(inventoryitem4.parent_id, data['parent'])
|
||||
self.assertEqual(inventoryitem4.name, data['name'])
|
||||
self.assertEqual(inventoryitem4.manufacturer_id, data['manufacturer'])
|
||||
self.assertEqual(inventoryitem4.site_id, data['site'])
|
||||
self.assertEqual(inventoryitem4.type_id, data['type'])
|
||||
self.assertEqual(inventoryitem4.role_id, data['role'])
|
||||
|
||||
def test_create_inventoryitem_bulk(self):
|
||||
|
||||
data = [
|
||||
{
|
||||
'device': self.device.pk,
|
||||
'site': self.site.pk,
|
||||
'type': self.inventoryitemtype.pk,
|
||||
'role': self.inventoryitemrole.pk,
|
||||
'parent': self.inventoryitem1.pk,
|
||||
'name': 'Test Inventory Item 4',
|
||||
'manufacturer': self.manufacturer.pk,
|
||||
},
|
||||
{
|
||||
'device': self.device.pk,
|
||||
'site': self.site.pk,
|
||||
'type': self.inventoryitemtype.pk,
|
||||
'role': self.inventoryitemrole.pk,
|
||||
'parent': self.inventoryitem1.pk,
|
||||
'name': 'Test Inventory Item 5',
|
||||
'manufacturer': self.manufacturer.pk,
|
||||
},
|
||||
{
|
||||
'device': self.device.pk,
|
||||
'site': self.site.pk,
|
||||
'type': self.inventoryitemtype.pk,
|
||||
'role': self.inventoryitemrole.pk,
|
||||
'parent': self.inventoryitem1.pk,
|
||||
'name': 'Test Inventory Item 6',
|
||||
'manufacturer': self.manufacturer.pk,
|
||||
},
|
||||
]
|
||||
|
||||
@ -3334,9 +3350,11 @@ class InventoryItemTest(APITestCase):
|
||||
|
||||
data = {
|
||||
'device': self.device.pk,
|
||||
'site': self.site.pk,
|
||||
'type': self.inventoryitemtype.pk,
|
||||
'role': self.inventoryitemrole.pk,
|
||||
'parent': self.inventoryitem1.pk,
|
||||
'name': 'Test Inventory Item X',
|
||||
'manufacturer': self.manufacturer.pk,
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:inventoryitem-detail', kwargs={'pk': self.inventoryitem1.pk})
|
||||
@ -3348,7 +3366,6 @@ class InventoryItemTest(APITestCase):
|
||||
self.assertEqual(inventoryitem1.device_id, data['device'])
|
||||
self.assertEqual(inventoryitem1.parent_id, data['parent'])
|
||||
self.assertEqual(inventoryitem1.name, data['name'])
|
||||
self.assertEqual(inventoryitem1.manufacturer_id, data['manufacturer'])
|
||||
|
||||
def test_delete_inventoryitem(self):
|
||||
|
||||
@ -4329,3 +4346,234 @@ class PowerFeedTest(APITestCase):
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual(PowerFeed.objects.count(), 5)
|
||||
|
||||
|
||||
class InventoryItemRoleTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super().setUp()
|
||||
|
||||
self.inventoryitemrole1 = InventoryItemRole.objects.create(
|
||||
name='Test Inventory Item Role 1', slug='test-inventory-item-role-1'
|
||||
)
|
||||
self.inventoryitem1 = InventoryItem.objects.create(name='Test Inventory Item 1', role=self.inventoryitemrole1)
|
||||
|
||||
self.inventoryitemrole2 = InventoryItemRole.objects.create(
|
||||
name='Test Inventory Item Role 2', slug='test-inventory-item-role-2'
|
||||
)
|
||||
self.inventoryitemrole3 = InventoryItemRole.objects.create(
|
||||
name='Test Inventory Item Role 3', slug='test-inventory-item-role-3'
|
||||
)
|
||||
|
||||
def test_get_inventoryitemrole(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemrole-detail', kwargs={'pk': self.inventoryitemrole1.pk})
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
self.assertEqual(response.data['name'], self.inventoryitemrole1.name)
|
||||
|
||||
def test_list_inventoryitemroles(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemrole-list')
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
self.assertEqual(response.data['count'], 3)
|
||||
|
||||
def test_list_inventoryitemroles_brief(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemrole-list')
|
||||
response = self.client.get('{}?brief=1'.format(url), **self.header)
|
||||
|
||||
self.assertEqual(
|
||||
sorted(response.data['results'][0]),
|
||||
['id', 'inventoryitem_count', 'name', 'slug', ]
|
||||
)
|
||||
|
||||
def test_create_inventoryitemrole(self):
|
||||
|
||||
data = {
|
||||
'name': 'Test Inventory Item Role 4',
|
||||
'slug': 'test-inventoryitem-role-4',
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:inventoryitemrole-list')
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||
self.assertEqual(InventoryItemRole.objects.count(), 4)
|
||||
inventoryitemrole4 = InventoryItemRole.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(inventoryitemrole4.name, data['name'])
|
||||
self.assertEqual(inventoryitemrole4.slug, data['slug'])
|
||||
|
||||
def test_create_inventoryitemrole_bulk(self):
|
||||
|
||||
data = [
|
||||
{
|
||||
'name': 'Test Inventory Item Role 4',
|
||||
'slug': 'test-inventoryitem-role-4',
|
||||
},
|
||||
{
|
||||
'name': 'Test Inventory Item Role 5',
|
||||
'slug': 'test-inventoryitem-role-5',
|
||||
},
|
||||
{
|
||||
'name': 'Test Inventory Item Role 6',
|
||||
'slug': 'test-inventoryitem-role-6',
|
||||
},
|
||||
]
|
||||
|
||||
url = reverse('dcim-api:inventoryitemrole-list')
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||
self.assertEqual(InventoryItemRole.objects.count(), 6)
|
||||
self.assertEqual(response.data[0]['name'], data[0]['name'])
|
||||
self.assertEqual(response.data[1]['name'], data[1]['name'])
|
||||
self.assertEqual(response.data[2]['name'], data[2]['name'])
|
||||
|
||||
def test_update_inventoryitemrole(self):
|
||||
|
||||
data = {
|
||||
'name': 'Test Inventory Item Role X',
|
||||
'slug': 'test-inventoryitem-role-x',
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:inventoryitemrole-detail', kwargs={'pk': self.inventoryitemrole1.pk})
|
||||
response = self.client.put(url, data, format='json', **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
self.assertEqual(InventoryItemRole.objects.count(), 3)
|
||||
inventoryitemrole1 = InventoryItemRole.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(inventoryitemrole1.name, data['name'])
|
||||
self.assertEqual(inventoryitemrole1.slug, data['slug'])
|
||||
|
||||
def test_delete_inventoryitemrole(self):
|
||||
url = reverse('dcim-api:inventoryitemrole-detail', kwargs={'pk': self.inventoryitemrole2.pk})
|
||||
response = self.client.delete(url, **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual(InventoryItemRole.objects.count(), 2)
|
||||
|
||||
def test_delete_inventoryitemrole_with_items(self):
|
||||
url = reverse('dcim-api:inventoryitemrole-detail', kwargs={'pk': self.inventoryitemrole1.pk})
|
||||
response = self.client.delete(url, **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_409_CONFLICT)
|
||||
self.assertEqual(InventoryItemRole.objects.count(), 3)
|
||||
|
||||
|
||||
class InventoryItemTypeTest(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
super().setUp()
|
||||
|
||||
self.manufacturer1 = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||
self.manufacturer2 = Manufacturer.objects.create(name='Test Manufacturer 2', slug='test-manufacturer-2')
|
||||
self.inventoryitemtype1 = InventoryItemType.objects.create(
|
||||
manufacturer=self.manufacturer1, model='Test Inventory Item Type 1', slug='test-inventory-item-type-1'
|
||||
)
|
||||
self.inventoryitemtype2 = InventoryItemType.objects.create(
|
||||
manufacturer=self.manufacturer1, model='Test Inventory Item Type 2', slug='test-inventory-item-type-2'
|
||||
)
|
||||
self.inventoryitemtype3 = InventoryItemType.objects.create(
|
||||
manufacturer=self.manufacturer1, model='Test Inventory Item Type 3', slug='test-inventory-item-type-3'
|
||||
)
|
||||
|
||||
def test_get_inventoryitemtype(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-detail', kwargs={'pk': self.inventoryitemtype1.pk})
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
self.assertEqual(response.data['model'], self.inventoryitemtype1.model)
|
||||
|
||||
def test_list_inventoryitemtypes(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-list')
|
||||
response = self.client.get(url, **self.header)
|
||||
|
||||
self.assertEqual(response.data['count'], 3)
|
||||
|
||||
def test_list_inventoryitemtypes_brief(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-list')
|
||||
response = self.client.get('{}?brief=1'.format(url), **self.header)
|
||||
|
||||
self.assertEqual(
|
||||
sorted(response.data['results'][0]),
|
||||
['id', 'instance_count', 'manufacturer', 'model', 'slug', 'url']
|
||||
)
|
||||
|
||||
def test_create_inventoryitemtype(self):
|
||||
|
||||
data = {
|
||||
'manufacturer': self.manufacturer1.pk,
|
||||
'model': 'Test Inventory Item Type 4',
|
||||
'slug': 'test-inventory-item-type-4',
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-list')
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||
self.assertEqual(InventoryItemType.objects.count(), 4)
|
||||
inventoryitemtype4 = InventoryItemType.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(inventoryitemtype4.manufacturer_id, data['manufacturer'])
|
||||
self.assertEqual(inventoryitemtype4.model, data['model'])
|
||||
self.assertEqual(inventoryitemtype4.slug, data['slug'])
|
||||
|
||||
def test_create_inventoryitemtype_bulk(self):
|
||||
|
||||
data = [
|
||||
{
|
||||
'manufacturer': self.manufacturer1.pk,
|
||||
'model': 'Test Inventory Item Type 4',
|
||||
'slug': 'test-inventory-item-type-4',
|
||||
},
|
||||
{
|
||||
'manufacturer': self.manufacturer1.pk,
|
||||
'model': 'Test Inventory Item Type 5',
|
||||
'slug': 'test-inventory-item-type-5',
|
||||
},
|
||||
{
|
||||
'manufacturer': self.manufacturer1.pk,
|
||||
'model': 'Test Inventory Item Type 6',
|
||||
'slug': 'test-inventory-item-type-6',
|
||||
},
|
||||
]
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-list')
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||
self.assertEqual(InventoryItemType.objects.count(), 6)
|
||||
self.assertEqual(response.data[0]['model'], data[0]['model'])
|
||||
self.assertEqual(response.data[1]['model'], data[1]['model'])
|
||||
self.assertEqual(response.data[2]['model'], data[2]['model'])
|
||||
|
||||
def test_update_inventoryitemtype(self):
|
||||
|
||||
data = {
|
||||
'manufacturer': self.manufacturer2.pk,
|
||||
'model': 'Test Inventory Item Type X',
|
||||
'slug': 'test-inventory-item-type-x',
|
||||
}
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-detail', kwargs={'pk': self.inventoryitemtype1.pk})
|
||||
response = self.client.put(url, data, format='json', **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
self.assertEqual(InventoryItemType.objects.count(), 3)
|
||||
inventoryitemtype1 = InventoryItemType.objects.get(pk=response.data['id'])
|
||||
self.assertEqual(inventoryitemtype1.manufacturer_id, data['manufacturer'])
|
||||
self.assertEqual(inventoryitemtype1.model, data['model'])
|
||||
self.assertEqual(inventoryitemtype1.slug, data['slug'])
|
||||
|
||||
def test_delete_inventoryitemtype(self):
|
||||
|
||||
url = reverse('dcim-api:inventoryitemtype-detail', kwargs={'pk': self.inventoryitemtype1.pk})
|
||||
response = self.client.delete(url, **self.header)
|
||||
|
||||
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual(InventoryItemType.objects.count(), 2)
|
||||
|
@ -6,9 +6,9 @@ from dcim.filters import *
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerPanel, PowerPort, PowerPortTemplate, PowerOutlet,
|
||||
PowerOutletTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
InventoryItem, InventoryItemRole, InventoryItemType, Manufacturer, Platform, PowerFeed, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, PowerOutlet, PowerOutletTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort,
|
||||
RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
from ipam.models import IPAddress
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
@ -67,7 +67,6 @@ class SiteTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -91,9 +90,15 @@ class SiteTestCase(TestCase):
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
sites = (
|
||||
Site(name='Site 1', slug='site-1', region=regions[0], tenant=tenants[0], status=SiteStatusChoices.STATUS_ACTIVE, facility='Facility 1', asn=65001, latitude=10, longitude=10, contact_name='Contact 1', contact_phone='123-555-0001', contact_email='contact1@example.com'),
|
||||
Site(name='Site 2', slug='site-2', region=regions[1], tenant=tenants[1], status=SiteStatusChoices.STATUS_PLANNED, facility='Facility 2', asn=65002, latitude=20, longitude=20, contact_name='Contact 2', contact_phone='123-555-0002', contact_email='contact2@example.com'),
|
||||
Site(name='Site 3', slug='site-3', region=regions[2], tenant=tenants[2], status=SiteStatusChoices.STATUS_RETIRED, facility='Facility 3', asn=65003, latitude=30, longitude=30, contact_name='Contact 3', contact_phone='123-555-0003', contact_email='contact3@example.com'),
|
||||
Site(name='Site 1', slug='site-1', region=regions[0], tenant=tenants[0],
|
||||
status=SiteStatusChoices.STATUS_ACTIVE, facility='Facility 1', asn=65001, latitude=10, longitude=10,
|
||||
contact_name='Contact 1', contact_phone='123-555-0001', contact_email='contact1@example.com'),
|
||||
Site(name='Site 2', slug='site-2', region=regions[1], tenant=tenants[1],
|
||||
status=SiteStatusChoices.STATUS_PLANNED, facility='Facility 2', asn=65002, latitude=20, longitude=20,
|
||||
contact_name='Contact 2', contact_phone='123-555-0002', contact_email='contact2@example.com'),
|
||||
Site(name='Site 3', slug='site-3', region=regions[2], tenant=tenants[2],
|
||||
status=SiteStatusChoices.STATUS_RETIRED, facility='Facility 3', asn=65003, latitude=30, longitude=30,
|
||||
contact_name='Contact 3', contact_phone='123-555-0003', contact_email='contact3@example.com'),
|
||||
)
|
||||
Site.objects.bulk_create(sites)
|
||||
|
||||
@ -175,7 +180,6 @@ class RackGroupTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -232,7 +236,6 @@ class RackRoleTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
rack_roles = (
|
||||
RackRole(name='Rack Role 1', slug='rack-role-1', color='ff0000'),
|
||||
RackRole(name='Rack Role 2', slug='rack-role-2', color='00ff00'),
|
||||
@ -264,7 +267,6 @@ class RackTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -309,9 +311,18 @@ class RackTestCase(TestCase):
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
racks = (
|
||||
Rack(name='Rack 1', facility_id='rack-1', site=sites[0], group=rack_groups[0], tenant=tenants[0], status=RackStatusChoices.STATUS_ACTIVE, role=rack_roles[0], serial='ABC', asset_tag='1001', type=RackTypeChoices.TYPE_2POST, width=RackWidthChoices.WIDTH_19IN, u_height=42, desc_units=False, outer_width=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER),
|
||||
Rack(name='Rack 2', facility_id='rack-2', site=sites[1], group=rack_groups[1], tenant=tenants[1], status=RackStatusChoices.STATUS_PLANNED, role=rack_roles[1], serial='DEF', asset_tag='1002', type=RackTypeChoices.TYPE_4POST, width=RackWidthChoices.WIDTH_19IN, u_height=43, desc_units=False, outer_width=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER),
|
||||
Rack(name='Rack 3', facility_id='rack-3', site=sites[2], group=rack_groups[2], tenant=tenants[2], status=RackStatusChoices.STATUS_RESERVED, role=rack_roles[2], serial='GHI', asset_tag='1003', type=RackTypeChoices.TYPE_CABINET, width=RackWidthChoices.WIDTH_23IN, u_height=44, desc_units=True, outer_width=300, outer_depth=300, outer_unit=RackDimensionUnitChoices.UNIT_INCH),
|
||||
Rack(name='Rack 1', facility_id='rack-1', site=sites[0], group=rack_groups[0], tenant=tenants[0],
|
||||
status=RackStatusChoices.STATUS_ACTIVE, role=rack_roles[0], serial='ABC', asset_tag='1001',
|
||||
type=RackTypeChoices.TYPE_2POST, width=RackWidthChoices.WIDTH_19IN, u_height=42, desc_units=False,
|
||||
outer_width=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER),
|
||||
Rack(name='Rack 2', facility_id='rack-2', site=sites[1], group=rack_groups[1], tenant=tenants[1],
|
||||
status=RackStatusChoices.STATUS_PLANNED, role=rack_roles[1], serial='DEF', asset_tag='1002',
|
||||
type=RackTypeChoices.TYPE_4POST, width=RackWidthChoices.WIDTH_19IN, u_height=43, desc_units=False,
|
||||
outer_width=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER),
|
||||
Rack(name='Rack 3', facility_id='rack-3', site=sites[2], group=rack_groups[2], tenant=tenants[2],
|
||||
status=RackStatusChoices.STATUS_RESERVED, role=rack_roles[2], serial='GHI', asset_tag='1003',
|
||||
type=RackTypeChoices.TYPE_CABINET, width=RackWidthChoices.WIDTH_23IN, u_height=44, desc_units=True,
|
||||
outer_width=300, outer_depth=300, outer_unit=RackDimensionUnitChoices.UNIT_INCH),
|
||||
)
|
||||
Rack.objects.bulk_create(racks)
|
||||
|
||||
@ -429,7 +440,6 @@ class RackReservationTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
sites = (
|
||||
Site(name='Site 1', slug='site-1'),
|
||||
Site(name='Site 2', slug='site-2'),
|
||||
@ -527,7 +537,6 @@ class ManufacturerTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturers = (
|
||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||
@ -555,7 +564,6 @@ class DeviceTypeTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturers = (
|
||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||
@ -564,9 +572,12 @@ class DeviceTypeTestCase(TestCase):
|
||||
Manufacturer.objects.bulk_create(manufacturers)
|
||||
|
||||
device_types = (
|
||||
DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1', u_height=1, is_full_depth=True),
|
||||
DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2', u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT),
|
||||
DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3', u_height=3, is_full_depth=False, subdevice_role=SubdeviceRoleChoices.ROLE_CHILD),
|
||||
DeviceType(manufacturer=manufacturers[0], model='Model 1', slug='model-1', part_number='Part Number 1',
|
||||
u_height=1, is_full_depth=True),
|
||||
DeviceType(manufacturer=manufacturers[1], model='Model 2', slug='model-2', part_number='Part Number 2',
|
||||
u_height=2, is_full_depth=True, subdevice_role=SubdeviceRoleChoices.ROLE_PARENT),
|
||||
DeviceType(manufacturer=manufacturers[2], model='Model 3', slug='model-3', part_number='Part Number 3',
|
||||
u_height=3, is_full_depth=False, subdevice_role=SubdeviceRoleChoices.ROLE_CHILD),
|
||||
)
|
||||
DeviceType.objects.bulk_create(device_types)
|
||||
|
||||
@ -597,8 +608,10 @@ class DeviceTypeTestCase(TestCase):
|
||||
)
|
||||
RearPortTemplate.objects.bulk_create(rear_ports)
|
||||
FrontPortTemplate.objects.bulk_create((
|
||||
FrontPortTemplate(device_type=device_types[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0]),
|
||||
FrontPortTemplate(device_type=device_types[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[1]),
|
||||
FrontPortTemplate(device_type=device_types[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C,
|
||||
rear_port=rear_ports[0]),
|
||||
FrontPortTemplate(device_type=device_types[1], name='Front Port 2', type=PortTypeChoices.TYPE_8P8C,
|
||||
rear_port=rear_ports[1]),
|
||||
))
|
||||
DeviceBayTemplate.objects.bulk_create((
|
||||
DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1'),
|
||||
@ -692,7 +705,6 @@ class ConsolePortTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -729,7 +741,6 @@ class ConsoleServerPortTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -766,7 +777,6 @@ class PowerPortTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -811,7 +821,6 @@ class PowerOutletTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -822,9 +831,12 @@ class PowerOutletTemplateTestCase(TestCase):
|
||||
DeviceType.objects.bulk_create(device_types)
|
||||
|
||||
PowerOutletTemplate.objects.bulk_create((
|
||||
PowerOutletTemplate(device_type=device_types[0], name='Power Outlet 1', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A),
|
||||
PowerOutletTemplate(device_type=device_types[1], name='Power Outlet 2', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B),
|
||||
PowerOutletTemplate(device_type=device_types[2], name='Power Outlet 3', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C),
|
||||
PowerOutletTemplate(device_type=device_types[0], name='Power Outlet 1',
|
||||
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A),
|
||||
PowerOutletTemplate(device_type=device_types[1], name='Power Outlet 2',
|
||||
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B),
|
||||
PowerOutletTemplate(device_type=device_types[2], name='Power Outlet 3',
|
||||
feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C),
|
||||
))
|
||||
|
||||
def test_id(self):
|
||||
@ -853,7 +865,6 @@ class InterfaceTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -864,9 +875,12 @@ class InterfaceTemplateTestCase(TestCase):
|
||||
DeviceType.objects.bulk_create(device_types)
|
||||
|
||||
InterfaceTemplate.objects.bulk_create((
|
||||
InterfaceTemplate(device_type=device_types[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED, mgmt_only=True),
|
||||
InterfaceTemplate(device_type=device_types[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_GBIC, mgmt_only=False),
|
||||
InterfaceTemplate(device_type=device_types[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_SFP, mgmt_only=False),
|
||||
InterfaceTemplate(device_type=device_types[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||
mgmt_only=True),
|
||||
InterfaceTemplate(device_type=device_types[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_GBIC,
|
||||
mgmt_only=False),
|
||||
InterfaceTemplate(device_type=device_types[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_SFP,
|
||||
mgmt_only=False),
|
||||
))
|
||||
|
||||
def test_id(self):
|
||||
@ -901,7 +915,6 @@ class FrontPortTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -919,9 +932,12 @@ class FrontPortTemplateTestCase(TestCase):
|
||||
RearPortTemplate.objects.bulk_create(rear_ports)
|
||||
|
||||
FrontPortTemplate.objects.bulk_create((
|
||||
FrontPortTemplate(device_type=device_types[0], name='Front Port 1', rear_port=rear_ports[0], type=PortTypeChoices.TYPE_8P8C),
|
||||
FrontPortTemplate(device_type=device_types[1], name='Front Port 2', rear_port=rear_ports[1], type=PortTypeChoices.TYPE_110_PUNCH),
|
||||
FrontPortTemplate(device_type=device_types[2], name='Front Port 3', rear_port=rear_ports[2], type=PortTypeChoices.TYPE_BNC),
|
||||
FrontPortTemplate(device_type=device_types[0], name='Front Port 1', rear_port=rear_ports[0],
|
||||
type=PortTypeChoices.TYPE_8P8C),
|
||||
FrontPortTemplate(device_type=device_types[1], name='Front Port 2', rear_port=rear_ports[1],
|
||||
type=PortTypeChoices.TYPE_110_PUNCH),
|
||||
FrontPortTemplate(device_type=device_types[2], name='Front Port 3', rear_port=rear_ports[2],
|
||||
type=PortTypeChoices.TYPE_BNC),
|
||||
))
|
||||
|
||||
def test_id(self):
|
||||
@ -950,7 +966,6 @@ class RearPortTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -961,9 +976,12 @@ class RearPortTemplateTestCase(TestCase):
|
||||
DeviceType.objects.bulk_create(device_types)
|
||||
|
||||
RearPortTemplate.objects.bulk_create((
|
||||
RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C, positions=1),
|
||||
RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PortTypeChoices.TYPE_110_PUNCH, positions=2),
|
||||
RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, positions=3),
|
||||
RearPortTemplate(device_type=device_types[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C,
|
||||
positions=1),
|
||||
RearPortTemplate(device_type=device_types[1], name='Rear Port 2', type=PortTypeChoices.TYPE_110_PUNCH,
|
||||
positions=2),
|
||||
RearPortTemplate(device_type=device_types[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC,
|
||||
positions=3),
|
||||
))
|
||||
|
||||
def test_id(self):
|
||||
@ -996,7 +1014,6 @@ class DeviceBayTemplateTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
|
||||
device_types = (
|
||||
@ -1033,7 +1050,6 @@ class DeviceRoleTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
device_roles = (
|
||||
DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000', vm_role=True),
|
||||
DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00', vm_role=True),
|
||||
@ -1071,7 +1087,6 @@ class PlatformTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturers = (
|
||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||
@ -1117,7 +1132,6 @@ class DeviceTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturers = (
|
||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||
@ -1198,9 +1212,16 @@ class DeviceTestCase(TestCase):
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
devices = (
|
||||
Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], platform=platforms[0], tenant=tenants[0], serial='ABC', asset_tag='1001', site=sites[0], rack=racks[0], position=1, face=DeviceFaceChoices.FACE_FRONT, status=DeviceStatusChoices.STATUS_ACTIVE, cluster=clusters[0], local_context_data={"foo": 123}),
|
||||
Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], platform=platforms[1], tenant=tenants[1], serial='DEF', asset_tag='1002', site=sites[1], rack=racks[1], position=2, face=DeviceFaceChoices.FACE_FRONT, status=DeviceStatusChoices.STATUS_STAGED, cluster=clusters[1]),
|
||||
Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], platform=platforms[2], tenant=tenants[2], serial='GHI', asset_tag='1003', site=sites[2], rack=racks[2], position=3, face=DeviceFaceChoices.FACE_REAR, status=DeviceStatusChoices.STATUS_FAILED, cluster=clusters[2]),
|
||||
Device(name='Device 1', device_type=device_types[0], device_role=device_roles[0], platform=platforms[0],
|
||||
tenant=tenants[0], serial='ABC', asset_tag='1001', site=sites[0], rack=racks[0], position=1,
|
||||
face=DeviceFaceChoices.FACE_FRONT, status=DeviceStatusChoices.STATUS_ACTIVE, cluster=clusters[0],
|
||||
local_context_data={"foo": 123}),
|
||||
Device(name='Device 2', device_type=device_types[1], device_role=device_roles[1], platform=platforms[1],
|
||||
tenant=tenants[1], serial='DEF', asset_tag='1002', site=sites[1], rack=racks[1], position=2,
|
||||
face=DeviceFaceChoices.FACE_FRONT, status=DeviceStatusChoices.STATUS_STAGED, cluster=clusters[1]),
|
||||
Device(name='Device 3', device_type=device_types[2], device_role=device_roles[2], platform=platforms[2],
|
||||
tenant=tenants[2], serial='GHI', asset_tag='1003', site=sites[2], rack=racks[2], position=3,
|
||||
face=DeviceFaceChoices.FACE_REAR, status=DeviceStatusChoices.STATUS_FAILED, cluster=clusters[2]),
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
@ -1452,7 +1473,6 @@ class ConsolePortTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -1548,7 +1568,6 @@ class ConsoleServerPortTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -1644,7 +1663,6 @@ class PowerPortTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -1678,8 +1696,10 @@ class PowerPortTestCase(TestCase):
|
||||
|
||||
power_ports = (
|
||||
PowerPort(device=devices[0], name='Power Port 1', maximum_draw=100, allocated_draw=50, description='First'),
|
||||
PowerPort(device=devices[1], name='Power Port 2', maximum_draw=200, allocated_draw=100, description='Second'),
|
||||
PowerPort(device=devices[2], name='Power Port 3', maximum_draw=300, allocated_draw=150, description='Third'),
|
||||
PowerPort(device=devices[1], name='Power Port 2', maximum_draw=200, allocated_draw=100,
|
||||
description='Second'),
|
||||
PowerPort(device=devices[2], name='Power Port 3', maximum_draw=300, allocated_draw=150,
|
||||
description='Third'),
|
||||
)
|
||||
PowerPort.objects.bulk_create(power_ports)
|
||||
|
||||
@ -1748,7 +1768,6 @@ class PowerOutletTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -1781,9 +1800,12 @@ class PowerOutletTestCase(TestCase):
|
||||
PowerPort.objects.bulk_create(power_ports)
|
||||
|
||||
power_outlets = (
|
||||
PowerOutlet(device=devices[0], name='Power Outlet 1', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, description='First'),
|
||||
PowerOutlet(device=devices[1], name='Power Outlet 2', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B, description='Second'),
|
||||
PowerOutlet(device=devices[2], name='Power Outlet 3', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C, description='Third'),
|
||||
PowerOutlet(device=devices[0], name='Power Outlet 1', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A,
|
||||
description='First'),
|
||||
PowerOutlet(device=devices[1], name='Power Outlet 2', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B,
|
||||
description='Second'),
|
||||
PowerOutlet(device=devices[2], name='Power Outlet 3', feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C,
|
||||
description='Third'),
|
||||
)
|
||||
PowerOutlet.objects.bulk_create(power_outlets)
|
||||
|
||||
@ -1849,7 +1871,6 @@ class InterfaceTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -1876,12 +1897,21 @@ class InterfaceTestCase(TestCase):
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
interfaces = (
|
||||
Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First'),
|
||||
Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second'),
|
||||
Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED, enabled=False, mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL, mac_address='00-00-00-00-00-03', description='Third'),
|
||||
Interface(device=devices[3], name='Interface 4', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True),
|
||||
Interface(device=devices[3], name='Interface 5', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True),
|
||||
Interface(device=devices[3], name='Interface 6', type=InterfaceTypeChoices.TYPE_OTHER, enabled=False, mgmt_only=False),
|
||||
Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True,
|
||||
mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01',
|
||||
description='First'),
|
||||
Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True,
|
||||
mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02',
|
||||
description='Second'),
|
||||
Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED, enabled=False,
|
||||
mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL,
|
||||
mac_address='00-00-00-00-00-03', description='Third'),
|
||||
Interface(device=devices[3], name='Interface 4', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True,
|
||||
mgmt_only=True),
|
||||
Interface(device=devices[3], name='Interface 5', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True,
|
||||
mgmt_only=True),
|
||||
Interface(device=devices[3], name='Interface 6', type=InterfaceTypeChoices.TYPE_OTHER, enabled=False,
|
||||
mgmt_only=False),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
@ -1976,7 +2006,6 @@ class FrontPortTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -2013,12 +2042,18 @@ class FrontPortTestCase(TestCase):
|
||||
RearPort.objects.bulk_create(rear_ports)
|
||||
|
||||
front_ports = (
|
||||
FrontPort(device=devices[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0], rear_port_position=1, description='First'),
|
||||
FrontPort(device=devices[1], name='Front Port 2', type=PortTypeChoices.TYPE_110_PUNCH, rear_port=rear_ports[1], rear_port_position=2, description='Second'),
|
||||
FrontPort(device=devices[2], name='Front Port 3', type=PortTypeChoices.TYPE_BNC, rear_port=rear_ports[2], rear_port_position=3, description='Third'),
|
||||
FrontPort(device=devices[3], name='Front Port 4', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[3], rear_port_position=1),
|
||||
FrontPort(device=devices[3], name='Front Port 5', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[4], rear_port_position=1),
|
||||
FrontPort(device=devices[3], name='Front Port 6', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[5], rear_port_position=1),
|
||||
FrontPort(device=devices[0], name='Front Port 1', type=PortTypeChoices.TYPE_8P8C, rear_port=rear_ports[0],
|
||||
rear_port_position=1, description='First'),
|
||||
FrontPort(device=devices[1], name='Front Port 2', type=PortTypeChoices.TYPE_110_PUNCH,
|
||||
rear_port=rear_ports[1], rear_port_position=2, description='Second'),
|
||||
FrontPort(device=devices[2], name='Front Port 3', type=PortTypeChoices.TYPE_BNC, rear_port=rear_ports[2],
|
||||
rear_port_position=3, description='Third'),
|
||||
FrontPort(device=devices[3], name='Front Port 4', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[3],
|
||||
rear_port_position=1),
|
||||
FrontPort(device=devices[3], name='Front Port 5', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[4],
|
||||
rear_port_position=1),
|
||||
FrontPort(device=devices[3], name='Front Port 6', type=PortTypeChoices.TYPE_FC, rear_port=rear_ports[5],
|
||||
rear_port_position=1),
|
||||
)
|
||||
FrontPort.objects.bulk_create(front_ports)
|
||||
|
||||
@ -2079,7 +2114,6 @@ class RearPortTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -2106,9 +2140,12 @@ class RearPortTestCase(TestCase):
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
rear_ports = (
|
||||
RearPort(device=devices[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C, positions=1, description='First'),
|
||||
RearPort(device=devices[1], name='Rear Port 2', type=PortTypeChoices.TYPE_110_PUNCH, positions=2, description='Second'),
|
||||
RearPort(device=devices[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, positions=3, description='Third'),
|
||||
RearPort(device=devices[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C, positions=1,
|
||||
description='First'),
|
||||
RearPort(device=devices[1], name='Rear Port 2', type=PortTypeChoices.TYPE_110_PUNCH, positions=2,
|
||||
description='Second'),
|
||||
RearPort(device=devices[2], name='Rear Port 3', type=PortTypeChoices.TYPE_BNC, positions=3,
|
||||
description='Third'),
|
||||
RearPort(device=devices[3], name='Rear Port 4', type=PortTypeChoices.TYPE_FC, positions=4),
|
||||
RearPort(device=devices[3], name='Rear Port 5', type=PortTypeChoices.TYPE_FC, positions=5),
|
||||
RearPort(device=devices[3], name='Rear Port 6', type=PortTypeChoices.TYPE_FC, positions=6),
|
||||
@ -2176,7 +2213,6 @@ class DeviceBayTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -2249,7 +2285,6 @@ class InventoryItemTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturers = (
|
||||
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
|
||||
Manufacturer(name='Manufacturer 2', slug='manufacturer-2'),
|
||||
@ -2257,6 +2292,29 @@ class InventoryItemTestCase(TestCase):
|
||||
)
|
||||
Manufacturer.objects.bulk_create(manufacturers)
|
||||
|
||||
roles = (
|
||||
InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
|
||||
InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
|
||||
InventoryItemRole(name='Inventory Item Role 3', slug='inventory-item-role-3')
|
||||
)
|
||||
InventoryItemRole.objects.bulk_create(roles)
|
||||
|
||||
types = (
|
||||
InventoryItemType(
|
||||
model='Inventory Item Type 1', manufacturer=manufacturers[0], part_number='101',
|
||||
slug='inventory-item-type=1'
|
||||
),
|
||||
InventoryItemType(
|
||||
model='Inventory Item Type 2', manufacturer=manufacturers[1], part_number='102',
|
||||
slug='inventory-item-type=2'
|
||||
),
|
||||
InventoryItemType(
|
||||
model='Inventory Item Type 3', manufacturer=manufacturers[2], part_number='103',
|
||||
slug='inventory-item-type=3'
|
||||
)
|
||||
)
|
||||
InventoryItemType.objects.bulk_create(types)
|
||||
|
||||
device_type = DeviceType.objects.create(manufacturer=manufacturers[0], model='Model 1', slug='model-1')
|
||||
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||
|
||||
@ -2283,9 +2341,18 @@ class InventoryItemTestCase(TestCase):
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
inventory_items = (
|
||||
InventoryItem(device=devices[0], manufacturer=manufacturers[0], name='Inventory Item 1', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First'),
|
||||
InventoryItem(device=devices[1], manufacturer=manufacturers[1], name='Inventory Item 2', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second'),
|
||||
InventoryItem(device=devices[2], manufacturer=manufacturers[2], name='Inventory Item 3', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third'),
|
||||
InventoryItem(
|
||||
device=devices[0], site=sites[0], type=types[0], role=roles[0], name='Inventory Item 1', part_id='1001',
|
||||
serial='ABC', asset_tag='1001', discovered=True, description='First'
|
||||
),
|
||||
InventoryItem(
|
||||
device=devices[1], site=sites[1], type=types[1], role=roles[1], name='Inventory Item 2', part_id='1002',
|
||||
serial='DEF', asset_tag='1002', discovered=True, description='Second'
|
||||
),
|
||||
InventoryItem(
|
||||
device=devices[2], site=sites[2], type=types[2], role=roles[2], name='Inventory Item 3', part_id='1003',
|
||||
serial='GHI', asset_tag='1003', discovered=False, description='Third'
|
||||
),
|
||||
)
|
||||
InventoryItem.objects.bulk_create(inventory_items)
|
||||
|
||||
@ -2347,11 +2414,18 @@ class InventoryItemTestCase(TestCase):
|
||||
params = {'parent_id': [parent_items[0].pk, parent_items[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_manufacturer(self):
|
||||
manufacturers = Manufacturer.objects.all()[:2]
|
||||
params = {'manufacturer_id': [manufacturers[0].pk, manufacturers[1].pk]}
|
||||
def test_role(self):
|
||||
roles = InventoryItemRole.objects.all()[:2]
|
||||
params = {'role_id': [roles[0].pk, roles[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'manufacturer': [manufacturers[0].slug, manufacturers[1].slug]}
|
||||
params = {'role': [roles[0].slug, roles[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_type(self):
|
||||
types = InventoryItemType.objects.all()[:2]
|
||||
params = {'type_id': [types[0].pk, types[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'type': [types[0].slug, types[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_serial(self):
|
||||
@ -2367,7 +2441,6 @@ class VirtualChassisTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||
device_type = DeviceType.objects.create(manufacturer=manufacturer, model='Model 1', slug='model-1')
|
||||
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||
@ -2438,7 +2511,6 @@ class CableTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
sites = (
|
||||
Site(name='Site 1', slug='site-1'),
|
||||
Site(name='Site 2', slug='site-2'),
|
||||
@ -2464,12 +2536,18 @@ class CableTestCase(TestCase):
|
||||
device_role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
|
||||
|
||||
devices = (
|
||||
Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=1, tenant=tenants[0]),
|
||||
Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0], position=2, tenant=tenants[0]),
|
||||
Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=1, tenant=tenants[1]),
|
||||
Device(name='Device 4', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1], position=2),
|
||||
Device(name='Device 5', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=1),
|
||||
Device(name='Device 6', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2], position=2),
|
||||
Device(name='Device 1', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0],
|
||||
position=1, tenant=tenants[0]),
|
||||
Device(name='Device 2', device_type=device_type, device_role=device_role, site=sites[0], rack=racks[0],
|
||||
position=2, tenant=tenants[0]),
|
||||
Device(name='Device 3', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1],
|
||||
position=1, tenant=tenants[1]),
|
||||
Device(name='Device 4', device_type=device_type, device_role=device_role, site=sites[1], rack=racks[1],
|
||||
position=2),
|
||||
Device(name='Device 5', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2],
|
||||
position=1),
|
||||
Device(name='Device 6', device_type=device_type, device_role=device_role, site=sites[2], rack=racks[2],
|
||||
position=2),
|
||||
)
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
@ -2490,12 +2568,24 @@ class CableTestCase(TestCase):
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
# Cables
|
||||
Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1', type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=10, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2', type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=20, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3', type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_CONNECTED, color='f44336', length=30, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4', type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_PLANNED, color='f44336', length=40, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=10, length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
||||
Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=20, length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
||||
Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1',
|
||||
type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=10,
|
||||
length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2',
|
||||
type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=20,
|
||||
length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3',
|
||||
type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_CONNECTED, color='f44336', length=30,
|
||||
length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4',
|
||||
type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_PLANNED, color='f44336', length=40,
|
||||
length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
|
||||
Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5',
|
||||
type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=10,
|
||||
length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
||||
Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6',
|
||||
type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=20,
|
||||
length_unit=CableLengthUnitChoices.UNIT_METER).save()
|
||||
|
||||
def test_id(self):
|
||||
id_list = self.queryset.values_list('id', flat=True)[:2]
|
||||
@ -2563,7 +2653,6 @@ class PowerPanelTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -2623,7 +2712,6 @@ class PowerFeedTestCase(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
regions = (
|
||||
Region(name='Region 1', slug='region-1'),
|
||||
Region(name='Region 2', slug='region-2'),
|
||||
@ -2654,9 +2742,18 @@ class PowerFeedTestCase(TestCase):
|
||||
PowerPanel.objects.bulk_create(power_panels)
|
||||
|
||||
power_feeds = (
|
||||
PowerFeed(power_panel=power_panels[0], rack=racks[0], name='Power Feed 1', status=PowerFeedStatusChoices.STATUS_ACTIVE, type=PowerFeedTypeChoices.TYPE_PRIMARY, supply=PowerFeedSupplyChoices.SUPPLY_AC, phase=PowerFeedPhaseChoices.PHASE_3PHASE, voltage=100, amperage=100, max_utilization=10),
|
||||
PowerFeed(power_panel=power_panels[1], rack=racks[1], name='Power Feed 2', status=PowerFeedStatusChoices.STATUS_FAILED, type=PowerFeedTypeChoices.TYPE_PRIMARY, supply=PowerFeedSupplyChoices.SUPPLY_AC, phase=PowerFeedPhaseChoices.PHASE_3PHASE, voltage=200, amperage=200, max_utilization=20),
|
||||
PowerFeed(power_panel=power_panels[2], rack=racks[2], name='Power Feed 3', status=PowerFeedStatusChoices.STATUS_OFFLINE, type=PowerFeedTypeChoices.TYPE_REDUNDANT, supply=PowerFeedSupplyChoices.SUPPLY_DC, phase=PowerFeedPhaseChoices.PHASE_SINGLE, voltage=300, amperage=300, max_utilization=30),
|
||||
PowerFeed(power_panel=power_panels[0], rack=racks[0], name='Power Feed 1',
|
||||
status=PowerFeedStatusChoices.STATUS_ACTIVE, type=PowerFeedTypeChoices.TYPE_PRIMARY,
|
||||
supply=PowerFeedSupplyChoices.SUPPLY_AC, phase=PowerFeedPhaseChoices.PHASE_3PHASE, voltage=100,
|
||||
amperage=100, max_utilization=10),
|
||||
PowerFeed(power_panel=power_panels[1], rack=racks[1], name='Power Feed 2',
|
||||
status=PowerFeedStatusChoices.STATUS_FAILED, type=PowerFeedTypeChoices.TYPE_PRIMARY,
|
||||
supply=PowerFeedSupplyChoices.SUPPLY_AC, phase=PowerFeedPhaseChoices.PHASE_3PHASE, voltage=200,
|
||||
amperage=200, max_utilization=20),
|
||||
PowerFeed(power_panel=power_panels[2], rack=racks[2], name='Power Feed 3',
|
||||
status=PowerFeedStatusChoices.STATUS_OFFLINE, type=PowerFeedTypeChoices.TYPE_REDUNDANT,
|
||||
supply=PowerFeedSupplyChoices.SUPPLY_DC, phase=PowerFeedPhaseChoices.PHASE_SINGLE, voltage=300,
|
||||
amperage=300, max_utilization=30),
|
||||
)
|
||||
PowerFeed.objects.bulk_create(power_feeds)
|
||||
|
||||
@ -2718,4 +2815,34 @@ class PowerFeedTestCase(TestCase):
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class InventoryItemRoleTestCase(TestCase):
|
||||
queryset = InventoryItemRole.objects.all()
|
||||
filterset = InventoryItemRoleFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
inventory_item_roles = (
|
||||
InventoryItemRole(name='Inventory Item Role 1', slug='inventory-item-role-1'),
|
||||
InventoryItemRole(name='Inventory Item Role 2', slug='inventory-item-role-2'),
|
||||
InventoryItemRole(name='Inventory Item Role 3', slug='inventory-item-role-3'),
|
||||
)
|
||||
InventoryItemRole.objects.bulk_create(inventory_item_roles)
|
||||
|
||||
def test_id(self):
|
||||
id_list = self.queryset.values_list('id', flat=True)[:2]
|
||||
params = {'id': [str(id) for id in id_list]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_name(self):
|
||||
params = {'name': ['Inventory Item Role 1', 'Inventory Item Role 2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_slug(self):
|
||||
params = {'slug': ['inventory-item-role-1', 'inventory-item-role-2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_slug1(self):
|
||||
params = {'slug': ['inventory-item-role-1', 'test-role']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
# TODO: Connection filters
|
||||
|
@ -1384,7 +1384,6 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
|
||||
cls.form_data = {
|
||||
'device': device.pk,
|
||||
'manufacturer': manufacturer.pk,
|
||||
'name': 'Inventory Item X',
|
||||
'parent': None,
|
||||
'discovered': False,
|
||||
@ -1398,7 +1397,6 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
cls.bulk_create_data = {
|
||||
'device': device.pk,
|
||||
'name_pattern': 'Inventory Item [4-6]',
|
||||
'manufacturer': manufacturer.pk,
|
||||
'parent': None,
|
||||
'discovered': False,
|
||||
'part_id': '123456',
|
||||
@ -1409,7 +1407,6 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'device': device.pk,
|
||||
'manufacturer': manufacturer.pk,
|
||||
'part_id': '123456',
|
||||
'description': 'New description',
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ from . import views
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, FrontPort, Interface, Manufacturer, Platform,
|
||||
PowerFeed, PowerPanel, PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site,
|
||||
VirtualChassis,
|
||||
VirtualChassis, InventoryItemRole, InventoryItemType,
|
||||
)
|
||||
|
||||
app_name = 'dcim'
|
||||
@ -303,6 +303,30 @@ urlpatterns = [
|
||||
path('inventory-items/<int:pk>/edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
|
||||
path('inventory-items/<int:pk>/delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
|
||||
|
||||
# Inventory item roles
|
||||
path('inventory-item-roles/', views.InventoryItemRoleListView.as_view(), name='inventoryitemrole_list'),
|
||||
path('inventory-item-roles/import/', views.InventoryItemRoleBulkImportView.as_view(),
|
||||
name='inventoryitemrole_import'),
|
||||
path('inventory-item-roles/add/', views.InventoryItemRoleCreateView.as_view(), name='inventoryitemrole_add'),
|
||||
path('inventory-item-roles/delete/', views.InventoryItemRoleBulkDeleteView.as_view(),
|
||||
name='inventoryitemrole_bulk_delete'),
|
||||
path('inventory-item-roles/<slug:slug>/edit/', views.InventoryItemRoleEditView.as_view(),
|
||||
name='inventoryitemrole_edit'),
|
||||
path('inventory-item-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(),
|
||||
name='inventoryitemrole_changelog',
|
||||
kwargs={'model': InventoryItemRole}),
|
||||
|
||||
# Inventory Item types
|
||||
path('inventory-item-types/', views.InventoryItemTypeListView.as_view(), name='inventoryitemtype_list'),
|
||||
path('inventory-item-types/add/', views.InventoryItemTypeCreateView.as_view(), name='inventoryitemtype_add'),
|
||||
path('inventory-item-types/import/', views.InventoryItemTypeImportView.as_view(), name='inventoryitemtype_import'),
|
||||
path('inventory-item-types/edit/', views.InventoryItemTypeBulkEditView.as_view(), name='inventoryitemtype_bulk_edit'),
|
||||
path('inventory-item-types/delete/', views.InventoryItemTypeBulkDeleteView.as_view(), name='inventoryitemtype_bulk_delete'),
|
||||
path('inventory-item-types/<int:pk>/', views.InventoryItemTypeView.as_view(), name='inventoryitemtype'),
|
||||
path('inventory-item-types/<int:pk>/edit/', views.InventoryItemTypeEditView.as_view(), name='inventoryitemtype_edit'),
|
||||
path('inventory-item-types/<int:pk>/delete/', views.InventoryItemTypeDeleteView.as_view(), name='inventoryitemtype_delete'),
|
||||
path('inventory-item-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='inventoryitemtype_changelog', kwargs={'model': InventoryItemType}),
|
||||
|
||||
# Cables
|
||||
path('cables/', views.CableListView.as_view(), name='cable_list'),
|
||||
path('cables/import/', views.CableBulkImportView.as_view(), name='cable_import'),
|
||||
|
@ -35,7 +35,7 @@ from .constants import NONCONNECTABLE_IFACE_TYPES
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
InventoryItem, InventoryItemRole, InventoryItemType, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
@ -552,7 +552,7 @@ class ManufacturerListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_manufacturer'
|
||||
queryset = Manufacturer.objects.annotate(
|
||||
devicetype_count=Count('device_types', distinct=True),
|
||||
inventoryitem_count=Count('inventory_items', distinct=True),
|
||||
inventoryitem_count=Count('inventory_item_types__instances', distinct=True),
|
||||
platform_count=Count('platforms', distinct=True),
|
||||
)
|
||||
table = tables.ManufacturerTable
|
||||
@ -1007,11 +1007,11 @@ class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
queryset = DeviceBayTemplate.objects.all()
|
||||
table = tables.DeviceBayTemplateTable
|
||||
|
||||
|
||||
#
|
||||
# Device roles
|
||||
#
|
||||
|
||||
|
||||
class DeviceRoleListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_devicerole'
|
||||
queryset = DeviceRole.objects.all()
|
||||
@ -1180,7 +1180,7 @@ class DeviceInventoryView(PermissionRequiredMixin, View):
|
||||
inventory_items = InventoryItem.objects.filter(
|
||||
device=device, parent=None
|
||||
).prefetch_related(
|
||||
'manufacturer', 'child_items'
|
||||
'type__manufacturer', 'child_items'
|
||||
)
|
||||
|
||||
return render(request, 'dcim/device_inventory.html', {
|
||||
@ -2264,14 +2264,50 @@ class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
|
||||
|
||||
return '\n'.join(csv_data)
|
||||
|
||||
#
|
||||
# Inventory item roles
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemRoleListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_inventoryitemrole'
|
||||
queryset = InventoryItemRole.objects.all()
|
||||
table = tables.InventoryItemRoleTable
|
||||
|
||||
|
||||
class InventoryItemRoleCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||
permission_required = 'dcim.add_inventoryitemrole'
|
||||
model = InventoryItemRole
|
||||
model_form = forms.InventoryItemRoleForm
|
||||
default_return_url = 'dcim:inventoryitemrole_list'
|
||||
|
||||
|
||||
class InventoryItemRoleBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
permission_required = 'dcim.add_inventoryitemrole'
|
||||
model_form = forms.InventoryItemRoleCSVForm
|
||||
table = tables.InventoryItemRoleTable
|
||||
default_return_url = 'dcim:inventoryitemrole_list'
|
||||
|
||||
|
||||
class InventoryItemRoleEditView(InventoryItemRoleCreateView):
|
||||
permission_required = 'dcim.change_inventoryitemrole'
|
||||
|
||||
|
||||
class InventoryItemRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_inventoryitemrole'
|
||||
queryset = InventoryItemRole.objects.all()
|
||||
filterset = filters.InventoryItemRoleFilterSet
|
||||
table = tables.InventoryItemRoleTable
|
||||
default_return_url = 'dcim:inventoryitemrole_list'
|
||||
|
||||
#
|
||||
# Inventory items
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_inventoryitem'
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'role', 'type__manufacturer')
|
||||
filterset = filters.InventoryItemFilterSet
|
||||
filterset_form = forms.InventoryItemFilterForm
|
||||
table = tables.InventoryItemTable
|
||||
@ -2306,7 +2342,7 @@ class InventoryItemBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
|
||||
class InventoryItemBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'dcim.change_inventoryitem'
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'type__manufacturer')
|
||||
filterset = filters.InventoryItemFilterSet
|
||||
table = tables.InventoryItemTable
|
||||
form = forms.InventoryItemBulkEditForm
|
||||
@ -2315,11 +2351,78 @@ class InventoryItemBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
|
||||
class InventoryItemBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_inventoryitem'
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer')
|
||||
queryset = InventoryItem.objects.prefetch_related('device', 'type__manufacturer')
|
||||
table = tables.InventoryItemTable
|
||||
template_name = 'dcim/inventoryitem_bulk_delete.html'
|
||||
default_return_url = 'dcim:inventoryitem_list'
|
||||
|
||||
#
|
||||
# Inventory Item types
|
||||
#
|
||||
|
||||
|
||||
class InventoryItemTypeListView(PermissionRequiredMixin, ObjectListView):
|
||||
permission_required = 'dcim.view_inventoryitemtype'
|
||||
queryset = InventoryItemType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
|
||||
filterset = filters.InventoryItemTypeFilterSet
|
||||
filterset_form = forms.InventoryItemTypeFilterForm
|
||||
table = tables.InventoryItemTypeTable
|
||||
|
||||
|
||||
class InventoryItemTypeView(PermissionRequiredMixin, View):
|
||||
permission_required = 'dcim.view_inventoryitemtype'
|
||||
|
||||
def get(self, request, pk):
|
||||
|
||||
inventoryitemtype = get_object_or_404(InventoryItemType, pk=pk)
|
||||
return render(request, 'dcim/inventoryitemtype.html', {
|
||||
'inventoryitemtype': inventoryitemtype,
|
||||
})
|
||||
|
||||
|
||||
class InventoryItemTypeCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||
permission_required = 'dcim.add_inventoryitemtype'
|
||||
model = InventoryItemType
|
||||
model_form = forms.InventoryItemTypeForm
|
||||
template_name = 'dcim/inventoryitemtype_edit.html'
|
||||
default_return_url = 'dcim:inventoryitemtype_list'
|
||||
|
||||
|
||||
class InventoryItemTypeEditView(InventoryItemTypeCreateView):
|
||||
permission_required = 'dcim.change_inventoryitemtype'
|
||||
|
||||
|
||||
class InventoryItemTypeDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||
permission_required = 'dcim.delete_inventoryitemtype'
|
||||
model = InventoryItemType
|
||||
default_return_url = 'dcim:inventoryitemtype_list'
|
||||
|
||||
|
||||
class InventoryItemTypeImportView(PermissionRequiredMixin, ObjectImportView):
|
||||
permission_required = [
|
||||
'dcim.add_inventoryitemtype',
|
||||
]
|
||||
model = InventoryItemType
|
||||
model_form = forms.InventoryItemTypeImportForm
|
||||
default_return_url = 'dcim:inventoryitemtype_import'
|
||||
|
||||
|
||||
class InventoryItemTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'dcim.change_inventoryitemtype'
|
||||
queryset = InventoryItemType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
|
||||
filterset = filters.InventoryItemTypeFilterSet
|
||||
table = tables.InventoryItemTypeTable
|
||||
form = forms.InventoryItemTypeBulkEditForm
|
||||
default_return_url = 'dcim:inventoryitemtype_list'
|
||||
|
||||
|
||||
class InventoryItemTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_inventoryitemtype'
|
||||
queryset = InventoryItemType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances'))
|
||||
filterset = filters.InventoryItemTypeFilterSet
|
||||
table = tables.InventoryItemTypeTable
|
||||
default_return_url = 'dcim:inventoryitemtype_list'
|
||||
|
||||
|
||||
#
|
||||
# Virtual chassis
|
||||
|
@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td style="padding-left: {{ indent|add:5 }}px">{{ item }}</td>
|
||||
<td>{% if not item.discovered %}<i class="fa fa-asterisk" title="Manually created"></i>{% endif %}</td>
|
||||
<td>{{ item.manufacturer|default:"" }}</td>
|
||||
<td>{{ item.type.manufacturer|default:"" }}</td>
|
||||
<td>{{ item.part_id }}</td>
|
||||
<td>{{ item.serial }}</td>
|
||||
<td>{{ item.asset_tag|default:"" }}</td>
|
||||
|
89
netbox/templates/dcim/inventoryitemtype.html
Normal file
89
netbox/templates/dcim/inventoryitemtype.html
Normal file
@ -0,0 +1,89 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
{% load custom_links %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block title %}{{ inventoryitemtype.manufacturer }} {{ inventoryitemtype.model }}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<div class="row noprint">
|
||||
<div class="col-md-12">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{% url 'dcim:inventoryitemtype_list' %}">Inventory Item Types</a></li>
|
||||
<li><a href="{% url 'dcim:inventoryitemtype_list' %}?manufacturer={{ inventoryitemtype.manufacturer.slug }}">{{ inventoryitemtype.manufacturer }}</a></li>
|
||||
<li>{{ inventoryitemtype.model }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right noprint">
|
||||
{% if perms.dcim.add_inventoryitemtype %}
|
||||
{% clone_button inventoryitemtype %}
|
||||
{% endif %}
|
||||
{% if perms.dcim.change_inventoryitemtype %}
|
||||
{% edit_button inventoryitemtype use_pk=True %}
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_inventoryitemtype %}
|
||||
{% delete_button inventoryitemtype use_pk=True %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<h1>{{ inventoryitemtype.manufacturer }} {{ inventoryitemtype.model }}</h1>
|
||||
{% include 'inc/created_updated.html' with obj=inventoryitemtype %}
|
||||
<div class="pull-right noprint">
|
||||
{% custom_links inventoryitemtype %}
|
||||
</div>
|
||||
<ul class="nav nav-tabs">
|
||||
<li role="presentation"{% if not active_tab %} class="active"{% endif %}>
|
||||
<a href="{{ inventoryitemtype.get_absolute_url }}">Inventory Item Type</a>
|
||||
</li>
|
||||
{% if perms.extras.view_objectchange %}
|
||||
<li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
|
||||
<a href="{% url 'dcim:inventoryitemtype_changelog' pk=inventoryitemtype.pk %}">Change Log</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Chassis</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Manufacturer</td>
|
||||
<td><a href="{% url 'dcim:inventoryitemtype_list' %}?manufacturer={{ inventoryitemtype.manufacturer.slug }}">{{ inventoryitemtype.manufacturer }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Model Name</td>
|
||||
<td>
|
||||
{{ inventoryitemtype.model }}<br/>
|
||||
<small class="text-muted">{{ inventoryitemtype.slug }}</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Part Number</td>
|
||||
<td>{{ inventoryitemtype.part_number|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% include 'inc/custom_fields_panel.html' with obj=inventoryitemtype %}
|
||||
{% include 'extras/inc/tags_panel.html' with tags=inventoryitemtype.tags.all url='dcim:inventoryitemtype_list' %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Comments</strong>
|
||||
</div>
|
||||
<div class="panel-body rendered-markdown">
|
||||
{% if inventoryitemtype.comments %}
|
||||
{{ inventoryitemtype.comments|gfm }}
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
22
netbox/templates/dcim/inventoryitemtype_edit.html
Normal file
22
netbox/templates/dcim/inventoryitemtype_edit.html
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends 'utilities/obj_edit.html' %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block form %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Inventory Item Type</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.manufacturer %}
|
||||
{% render_field form.model %}
|
||||
{% render_field form.slug %}
|
||||
{% render_field form.part_number %}
|
||||
</div>
|
||||
</div>
|
||||
{% if form.custom_fields %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Custom Fields</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_custom_fields form %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -169,6 +169,25 @@
|
||||
{% endif %}
|
||||
<a href="{% url 'dcim:inventoryitem_list' %}">Inventory Items</a>
|
||||
</li>
|
||||
<li{% if not perms.dcim.view_inventoryitemrole %} class="disabled"{% endif %}>
|
||||
{% if perms.dcim.add_inventoryitemrole %}
|
||||
<div class="buttons pull-right">
|
||||
<a href="{% url 'dcim:inventoryitemrole_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
|
||||
<a href="{% url 'dcim:inventoryitemrole_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'dcim:inventoryitemrole_list' %}">Inventory Item Roles</a>
|
||||
</li>
|
||||
</li>
|
||||
<li{% if not perms.dcim.view_inventoryitemtype %} class="disabled"{% endif %}>
|
||||
{% if perms.dcim.add_inventoryitemtype %}
|
||||
<div class="buttons pull-right">
|
||||
<a href="{% url 'dcim:inventoryitemtype_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
|
||||
<a href="{% url 'dcim:inventoryitemtype_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'dcim:inventoryitemtype_list' %}">Inventory Item Types</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Connections</li>
|
||||
<li{% if not perms.dcim.view_cable %} class="disabled"{% endif %}>
|
||||
|
@ -27,7 +27,7 @@ fi
|
||||
# - E501: line greater than 80 characters in length
|
||||
pycodestyle \
|
||||
--ignore=W504,E501 \
|
||||
netbox/
|
||||
netbox
|
||||
RC=$?
|
||||
if [[ $RC != 0 ]]; then
|
||||
echo -e "\n$(info) one or more PEP 8 errors detected, failing build."
|
||||
|
Loading…
Reference in New Issue
Block a user