Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/models/dcim/inventoryitem.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ The serial number assigned by the manufacturer.
### Asset Tag

A unique, locally-administered label used to identify hardware resources.

### Status

The inventory item's operational status.
3 changes: 2 additions & 1 deletion netbox/dcim/api/serializers_/device_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,11 +345,12 @@ class InventoryItemSerializer(NetBoxModelSerializer):
)
component = serializers.SerializerMethodField(read_only=True, allow_null=True)
_depth = serializers.IntegerField(source='level', read_only=True)
status = ChoiceField(choices=InventoryItemStatusChoices, required=False)

class Meta:
model = InventoryItem
fields = [
'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer',
'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'status', 'role', 'manufacturer',
'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type', 'component_id',
'component', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
]
Expand Down
24 changes: 24 additions & 0 deletions netbox/dcim/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -1648,3 +1648,27 @@ class VirtualDeviceContextStatusChoices(ChoiceSet):
(STATUS_PLANNED, _('Planned'), 'cyan'),
(STATUS_OFFLINE, _('Offline'), 'red'),
]


#
# InventoryItem
#

class InventoryItemStatusChoices(ChoiceSet):
key = 'InventoryItem.status'

STATUS_OFFLINE = 'offline'
STATUS_ACTIVE = 'active'
STATUS_PLANNED = 'planned'
STATUS_STAGED = 'staged'
STATUS_FAILED = 'failed'
STATUS_DECOMMISSIONING = 'decommissioning'

CHOICES = [
(STATUS_OFFLINE, _('Offline'), 'gray'),
(STATUS_ACTIVE, _('Active'), 'green'),
(STATUS_PLANNED, _('Planned'), 'cyan'),
(STATUS_STAGED, _('Staged'), 'blue'),
(STATUS_FAILED, _('Failed'), 'red'),
(STATUS_DECOMMISSIONING, _('Decommissioning'), 'yellow'),
]
6 changes: 5 additions & 1 deletion netbox/dcim/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1860,10 +1860,14 @@ class InventoryItemFilterSet(DeviceComponentFilterSet, NetBoxModelFilterSet):
serial = MultiValueCharFilter(
lookup_expr='iexact'
)
status = django_filters.MultipleChoiceFilter(
choices=InventoryItemStatusChoices,
null_value=None
)

class Meta:
model = InventoryItem
fields = ('id', 'name', 'label', 'part_id', 'asset_tag', 'description', 'discovered')
fields = ('id', 'name', 'label', 'part_id', 'asset_tag', 'status', 'description', 'discovered')

def search(self, queryset, name, value):
if not value.strip():
Expand Down
8 changes: 7 additions & 1 deletion netbox/dcim/forms/bulk_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,10 +1661,16 @@ class InventoryItemBulkEditForm(
queryset=Manufacturer.objects.all(),
required=False
)
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(InventoryItemStatusChoices),
required=False,
initial=''
)

model = InventoryItem
fieldsets = (
FieldSet('device', 'label', 'role', 'manufacturer', 'part_id', 'description'),
FieldSet('device', 'label', 'role', 'manufacturer', 'part_id', 'status', 'description'),
)
nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description')

Expand Down
7 changes: 6 additions & 1 deletion netbox/dcim/forms/bulk_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -1103,11 +1103,16 @@ class InventoryItemImportForm(NetBoxModelImportForm):
required=False,
help_text=_('Component Name')
)
status = CSVChoiceField(
label=_('Status'),
choices=InventoryItemStatusChoices,
help_text=_('Operational status')
)

class Meta:
model = InventoryItem
fields = (
'device', 'name', 'label', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag', 'discovered',
'device', 'name', 'label', 'status', 'role', 'manufacturer', 'parent', 'part_id', 'serial', 'asset_tag', 'discovered',
'description', 'tags', 'component_type', 'component_name',
)

Expand Down
6 changes: 5 additions & 1 deletion netbox/dcim/forms/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
'LocationFilterForm',
'ManufacturerFilterForm',
'ModuleFilterForm',
'ModuleFilterForm',
'ModuleBayFilterForm',
'ModuleTypeFilterForm',
'PlatformFilterForm',
Expand Down Expand Up @@ -1553,6 +1552,11 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
status = forms.MultipleChoiceField(
label=_('Status'),
choices=InventoryItemStatusChoices,
required=False
)
tag = TagFilterField(model)


Expand Down
4 changes: 2 additions & 2 deletions netbox/dcim/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1576,7 +1576,7 @@ class InventoryItemForm(DeviceComponentForm):
)

fieldsets = (
FieldSet('device', 'parent', 'name', 'label', 'role', 'description', 'tags', name=_('Inventory Item')),
FieldSet('device', 'parent', 'name', 'label', 'status', 'role', 'description', 'tags', name=_('Inventory Item')),
FieldSet('manufacturer', 'part_id', 'serial', 'asset_tag', name=_('Hardware')),
FieldSet(
TabbedGroups(
Expand All @@ -1596,7 +1596,7 @@ class Meta:
model = InventoryItem
fields = [
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
'description', 'tags',
'status', 'description', 'tags',
]

def __init__(self, *args, **kwargs):
Expand Down
18 changes: 18 additions & 0 deletions netbox/dcim/migrations/0191_inventoryitem_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.9 on 2024-09-26 20:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dcim', '0190_nested_modules'),
]

operations = [
migrations.AddField(
model_name='inventoryitem',
name='status',
field=models.CharField(default='active', max_length=50),
),
]
11 changes: 10 additions & 1 deletion netbox/dcim/models/device_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,12 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
ct_field='component_type',
fk_field='component_id'
)
status = models.CharField(
verbose_name=_('status'),
max_length=50,
choices=InventoryItemStatusChoices,
default=InventoryItemStatusChoices.STATUS_ACTIVE
)
role = models.ForeignKey(
to='dcim.InventoryItemRole',
on_delete=models.PROTECT,
Expand Down Expand Up @@ -1285,7 +1291,7 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):

objects = TreeManager()

clone_fields = ('device', 'parent', 'role', 'manufacturer', 'part_id',)
clone_fields = ('device', 'parent', 'role', 'manufacturer', 'status', 'part_id')

class Meta:
ordering = ('device__id', 'parent__id', '_name')
Expand Down Expand Up @@ -1334,3 +1340,6 @@ def clean(self):
raise ValidationError({
"device": _("Cannot assign inventory item to component on another device")
})

def get_status_color(self):
return InventoryItemStatusChoices.colors.get(self.status)
11 changes: 7 additions & 4 deletions netbox/dcim/tables/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,9 @@ class InventoryItemTable(DeviceComponentTable):
verbose_name=_('Discovered'),
false_mark=None
)
status = columns.ChoiceFieldColumn(
verbose_name=_('Status'),
)
parent = tables.Column(
linkify=True,
verbose_name=_('Parent'),
Expand All @@ -958,11 +961,11 @@ class InventoryItemTable(DeviceComponentTable):
class Meta(NetBoxTable.Meta):
model = models.InventoryItem
fields = (
'pk', 'id', 'name', 'device', 'parent', 'component', 'label', 'role', 'manufacturer', 'part_id', 'serial',
'pk', 'id', 'name', 'device', 'parent', 'component', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial',
'asset_tag', 'description', 'discovered', 'tags', 'created', 'last_updated',
)
default_columns = (
'pk', 'name', 'device', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
'pk', 'name', 'device', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
)


Expand All @@ -978,11 +981,11 @@ class DeviceInventoryItemTable(InventoryItemTable):
class Meta(NetBoxTable.Meta):
model = models.InventoryItem
fields = (
'pk', 'id', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
'pk', 'id', 'name', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
'description', 'discovered', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
'pk', 'name', 'label', 'status', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'component',
)


Expand Down
10 changes: 7 additions & 3 deletions netbox/dcim/tests/test_filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -4751,9 +4751,9 @@ def setUpTestData(cls):
)

inventory_items = (
InventoryItem(device=devices[0], role=roles[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, description='First', component=components[0]),
InventoryItem(device=devices[1], role=roles[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, description='Second', component=components[1]),
InventoryItem(device=devices[2], role=roles[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, description='Third', component=components[2]),
InventoryItem(device=devices[0], role=roles[0], manufacturer=manufacturers[0], name='Inventory Item 1', label='A', part_id='1001', serial='ABC', asset_tag='1001', discovered=True, status=ModuleStatusChoices.STATUS_ACTIVE, description='First', component=components[0]),
InventoryItem(device=devices[1], role=roles[1], manufacturer=manufacturers[1], name='Inventory Item 2', label='B', part_id='1002', serial='DEF', asset_tag='1002', discovered=True, status=ModuleStatusChoices.STATUS_PLANNED, description='Second', component=components[1]),
InventoryItem(device=devices[2], role=roles[2], manufacturer=manufacturers[2], name='Inventory Item 3', label='C', part_id='1003', serial='GHI', asset_tag='1003', discovered=False, status=ModuleStatusChoices.STATUS_FAILED, description='Third', component=components[2]),
)
for i in inventory_items:
i.save()
Expand Down Expand Up @@ -4881,6 +4881,10 @@ def test_component_type(self):
params = {'component_type': 'dcim.interface'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)

def test_status(self):
params = {'status': [InventoryItemStatusChoices.STATUS_PLANNED, InventoryItemStatusChoices.STATUS_FAILED]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)


class InventoryItemRoleTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = InventoryItemRole.objects.all()
Expand Down
10 changes: 6 additions & 4 deletions netbox/dcim/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2903,6 +2903,7 @@ def setUpTestData(cls):
'part_id': '123456',
'serial': '123ABC',
'asset_tag': 'ABC123',
'status': InventoryItemStatusChoices.STATUS_ACTIVE,
'description': 'An inventory item',
'tags': [t.pk for t in tags],
}
Expand All @@ -2916,6 +2917,7 @@ def setUpTestData(cls):
'discovered': False,
'part_id': '123456',
'serial': '123ABC',
'status': InventoryItemStatusChoices.STATUS_ACTIVE,
'description': 'An inventory item',
'tags': [t.pk for t in tags],
}
Expand All @@ -2927,10 +2929,10 @@ def setUpTestData(cls):
}

cls.csv_data = (
"device,name,parent",
"Device 1,Inventory Item 4,Inventory Item 1",
"Device 1,Inventory Item 5,Inventory Item 2",
"Device 1,Inventory Item 6,Inventory Item 3",
"device,name,parent,status",
"Device 1,Inventory Item 4,Inventory Item 1,active",
"Device 1,Inventory Item 5,Inventory Item 2,planned",
"Device 1,Inventory Item 6,Inventory Item 3,failed",
)

cls.csv_update_data = (
Expand Down
4 changes: 4 additions & 0 deletions netbox/templates/dcim/inventoryitem.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ <h2 class="card-header">{% trans "Inventory Item" %}</h2>
<th scope="row">{% trans "Asset Tag" %}</th>
<td>{{ object.asset_tag|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Status" %}</th>
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
Expand Down