diff --git a/docs/installation/netbox.md b/docs/installation/netbox.md index dbfc23133db..b5b96d03489 100644 --- a/docs/installation/netbox.md +++ b/docs/installation/netbox.md @@ -10,9 +10,10 @@ NetBox requires following system dependencies: * libffi-dev * graphviz * libpq-dev +* libssl-dev ``` -# sudo apt-get install -y python2.7 python-dev git python-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev +# sudo apt-get install -y python2.7 python-dev python-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev ``` You may opt to install NetBox either from a numbered release or by cloning the master branch of its repository on GitHub. diff --git a/netbox/dcim/admin.py b/netbox/dcim/admin.py index 001a685c70c..9fd16a87de7 100644 --- a/netbox/dcim/admin.py +++ b/netbox/dcim/admin.py @@ -183,7 +183,8 @@ class DeviceAdmin(admin.ModelAdmin): DeviceBayAdmin, ModuleAdmin, ] - list_display = ['display_name', 'device_type', 'device_role', 'primary_ip', 'rack', 'position', 'serial'] + list_display = ['display_name', 'device_type', 'device_role', 'primary_ip', 'rack', 'position', 'asset_tag', + 'serial'] list_filter = ['device_role'] def get_queryset(self, request): diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index a0a2776f15d..706bf4a5223 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -250,8 +250,9 @@ class DeviceSerializer(serializers.ModelSerializer): class Meta: model = Device - fields = ['id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'rack', - 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6', 'comments'] + fields = ['id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', + 'asset_tag', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', + 'primary_ip6', 'comments'] def get_parent_device(self, obj): try: diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index b124d53dd42..3033ce564bc 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -239,15 +239,16 @@ class DeviceFilter(django_filters.FilterSet): class Meta: model = Device - fields = ['q', 'name', 'site_id', 'site', 'rack_id', 'role_id', 'role', 'device_type_id', 'manufacturer_id', - 'manufacturer', 'model', 'platform_id', 'platform', 'status', 'is_console_server', 'is_pdu', - 'is_network_device'] + fields = ['q', 'name', 'serial', 'asset_tag', 'site_id', 'site', 'rack_id', 'role_id', 'role', 'device_type_id', + 'manufacturer_id', 'manufacturer', 'model', 'platform_id', 'platform', 'status', 'is_console_server', + 'is_pdu', 'is_network_device'] def search(self, queryset, value): return queryset.filter( Q(name__icontains=value) | - Q(serial__icontains=value) | - Q(modules__serial__icontains=value) | + Q(serial__icontains=value.strip()) | + Q(modules__serial__icontains=value.strip()) | + Q(asset_tag=value.strip()) | Q(comments__icontains=value) ).distinct() diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 56e786a26e1..5d579c70973 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -425,8 +425,8 @@ class DeviceForm(forms.ModelForm, BootstrapMixin): class Meta: model = Device - fields = ['name', 'device_role', 'tenant', 'device_type', 'serial', 'site', 'rack', 'position', 'face', 'status', - 'platform', 'primary_ip4', 'primary_ip6', 'comments'] + fields = ['name', 'device_role', 'tenant', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'position', + 'face', 'status', 'platform', 'primary_ip4', 'primary_ip6', 'comments'] help_texts = { 'device_role': "The function this device serves", 'serial': "Chassis serial number", @@ -546,8 +546,8 @@ class DeviceFromCSVForm(BaseDeviceFromCSVForm): face = forms.CharField(required=False) class Meta(BaseDeviceFromCSVForm.Meta): - fields = ['name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'site', - 'rack_name', 'position', 'face'] + fields = ['name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', + 'site', 'rack_name', 'position', 'face'] def clean(self): @@ -582,8 +582,8 @@ class ChildDeviceFromCSVForm(BaseDeviceFromCSVForm): device_bay_name = forms.CharField(required=False) class Meta(BaseDeviceFromCSVForm.Meta): - fields = ['name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'parent', - 'device_bay_name'] + fields = ['name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', + 'parent', 'device_bay_name'] def clean(self): diff --git a/netbox/dcim/migrations/0018_device_add_asset_tag.py b/netbox/dcim/migrations/0018_device_add_asset_tag.py new file mode 100644 index 00000000000..706b42ac4d1 --- /dev/null +++ b/netbox/dcim/migrations/0018_device_add_asset_tag.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2016-08-11 15:42 +from __future__ import unicode_literals + +from django.db import migrations +import utilities.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0017_rack_add_role'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='asset_tag', + field=utilities.fields.NullableCharField(blank=True, help_text=b'A unique tag used to identify this device', max_length=50, null=True, unique=True, verbose_name=b'Asset tag'), + ), + ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 537d33087b5..b1c6b60b71e 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -373,7 +373,7 @@ def to_csv(self): self.tenant.name if self.tenant else '', self.role.name if self.role else '', self.get_type_display() if self.type else '', - self.width, + str(self.width), str(self.u_height), ]) @@ -737,6 +737,8 @@ class Device(CreatedUpdatedModel): platform = models.ForeignKey('Platform', related_name='devices', blank=True, null=True, on_delete=models.SET_NULL) name = NullableCharField(max_length=50, blank=True, null=True, unique=True) serial = models.CharField(max_length=50, blank=True, verbose_name='Serial number') + asset_tag = NullableCharField(max_length=50, blank=True, null=True, unique=True, verbose_name='Asset tag', + help_text='A unique tag used to identify this device') rack = models.ForeignKey('Rack', related_name='devices', on_delete=models.PROTECT) position = models.PositiveSmallIntegerField(blank=True, null=True, validators=[MinValueValidator(1)], verbose_name='Position (U)', @@ -832,6 +834,7 @@ def to_csv(self): self.device_type.model, self.platform.name if self.platform else '', self.serial, + self.asset_tag if self.asset_tag else '', self.rack.site.name, self.rack.name, str(self.position) if self.position else '', diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 0a049ce942e..9906a398e0f 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from utilities.tables import BaseTable, ColorColumn, ToggleColumn +from utilities.tables import BaseTable, ToggleColumn from .models import ( ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType, @@ -10,6 +10,10 @@ ) +COLOR_LABEL = """ + +""" + DEVICE_LINK = """ {{ record.name|default:'Unnamed device' }} @@ -28,6 +32,14 @@ {% endif %} """ +RACK_ROLE = """ +{% if record.role %} + +{% else %} + — +{% endif %} +""" + DEVICEROLE_ACTIONS = """ {% if perms.dcim.change_devicerole %} @@ -46,6 +58,10 @@ {% endif %} """ +DEVICE_ROLE = """ + +""" + STATUS_ICON = """ {% if record.status %} @@ -108,7 +124,7 @@ class RackRoleTable(BaseTable): pk = ToggleColumn() name = tables.LinkColumn(verbose_name='Name') rack_count = tables.Column(verbose_name='Racks') - color = ColorColumn(verbose_name='Color') + color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color') slug = tables.Column(verbose_name='Slug') actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='') @@ -129,7 +145,7 @@ class RackTable(BaseTable): group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') facility_id = tables.Column(verbose_name='Facility ID') tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - role = tables.Column(verbose_name='Role') + role = tables.TemplateColumn(RACK_ROLE, verbose_name='Role') u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height') devices = tables.Column(accessor=Accessor('device_count'), verbose_name='Devices') u_consumed = tables.TemplateColumn("{{ record.u_consumed|default:'0' }}U", verbose_name='Used') @@ -258,7 +274,7 @@ class DeviceRoleTable(BaseTable): pk = ToggleColumn() name = tables.LinkColumn(verbose_name='Name') device_count = tables.Column(verbose_name='Devices') - color = ColorColumn(verbose_name='Color') + color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color') slug = tables.Column(verbose_name='Slug') actions = tables.TemplateColumn(template_code=DEVICEROLE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='') @@ -295,7 +311,7 @@ class DeviceTable(BaseTable): tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') site = tables.Column(accessor=Accessor('rack.site'), verbose_name='Site') rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')], verbose_name='Rack') - device_role = tables.Column(verbose_name='Role') + device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role') device_type = tables.Column(verbose_name='Type') primary_ip = tables.TemplateColumn(orderable=False, verbose_name='IP Address', template_code="{{ record.primary_ip.address.ip }}") diff --git a/netbox/dcim/tests/test_apis.py b/netbox/dcim/tests/test_apis.py index bc9ea654740..b8ddf942105 100644 --- a/netbox/dcim/tests/test_apis.py +++ b/netbox/dcim/tests/test_apis.py @@ -327,6 +327,7 @@ class DeviceTest(APITestCase): 'tenant', 'platform', 'serial', + 'asset_tag', 'rack', 'position', 'face', @@ -370,6 +371,7 @@ def test_get_list(self, endpoint='/api/dcim/devices/'): def test_get_list_flat(self, endpoint='/api/dcim/devices/?format=json_flat'): flat_fields = [ + 'asset_tag', 'comments', 'device_role_id', 'device_role_name', diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 4891d5ce5b9..67f808745f2 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ "the documentation.") -VERSION = '1.5.0' +VERSION = '1.5.1' # Import local configuration for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index a2594426253..04955557155 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -60,7 +60,7 @@
rack101_sw1,ToR Switch,Pied Piper,Juniper,EX4300-48T,Juniper Junos,CAB00577291,Ashburn-VA,R101,21,Rear+
rack101_sw1,ToR Switch,Pied Piper,Juniper,EX4300-48T,Juniper Junos,CAB00577291,ABC123456,Ashburn-VA,R101,21,Rear{% endblock %} diff --git a/netbox/templates/dcim/device_import_child.html b/netbox/templates/dcim/device_import_child.html index d01c376536c..eed987d4636 100644 --- a/netbox/templates/dcim/device_import_child.html +++ b/netbox/templates/dcim/device_import_child.html @@ -57,10 +57,15 @@
Blade12,Blade Server,Pied Piper,Dell,BS2000T,Linux,CAB00577291,Server101,Slot4+
Blade12,Blade Server,Pied Piper,Dell,BS2000T,Linux,CAB00577291,ABC123456,Server101,Slot4{% endblock %} diff --git a/netbox/templates/dcim/device_inventory.html b/netbox/templates/dcim/device_inventory.html index a867d9eb372..b43083e7186 100644 --- a/netbox/templates/dcim/device_inventory.html +++ b/netbox/templates/dcim/device_inventory.html @@ -17,7 +17,23 @@