diff --git a/docs/models/dcim/location.md b/docs/models/dcim/location.md index 96ab1303910..2f520c57216 100644 --- a/docs/models/dcim/location.md +++ b/docs/models/dcim/location.md @@ -24,5 +24,9 @@ A unique URL-friendly identifier. (This value can be used for filtering.) The location's operational status. +### Facility + +Data center or facility designation for identifying the site. + !!! tip Additional statuses may be defined by setting `Location.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. diff --git a/netbox/dcim/api/serializers_/sites.py b/netbox/dcim/api/serializers_/sites.py index 6fb3811ba8f..8063278a7f1 100644 --- a/netbox/dcim/api/serializers_/sites.py +++ b/netbox/dcim/api/serializers_/sites.py @@ -92,7 +92,7 @@ class LocationSerializer(NestedGroupModelSerializer): class Meta: model = Location fields = [ - 'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'description', 'tags', - 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', '_depth', + 'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility', 'description', + 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth') diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index aa8a6829625..e721e1ebfe4 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -270,7 +270,7 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM class Meta: model = Location - fields = ('id', 'name', 'slug', 'status', 'description') + fields = ('id', 'name', 'slug', 'status', 'facility', 'description') def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 47974096fad..d4997308289 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -157,7 +157,7 @@ class LocationImportForm(NetBoxModelImportForm): class Meta: model = Location - fields = ('site', 'parent', 'name', 'slug', 'status', 'tenant', 'description', 'tags') + fields = ('site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description', 'tags') def __init__(self, data=None, *args, **kwargs): super().__init__(data, *args, **kwargs) diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 6c33ea8d67d..92740ec451d 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -179,14 +179,14 @@ class LocationForm(TenancyForm, NetBoxModelForm): slug = SlugField() fieldsets = ( - (_('Location'), ('site', 'parent', 'name', 'slug', 'status', 'description', 'tags')), + (_('Location'), ('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags')), (_('Tenancy'), ('tenant_group', 'tenant')), ) class Meta: model = Location fields = ( - 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'tags', + 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'tags', ) diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index d2797bf9510..c1da807adf6 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -275,6 +275,12 @@ class Location(ContactsMixin, ImageAttachmentsMixin, NestedGroupModel): blank=True, null=True ) + facility = models.CharField( + verbose_name=_('facility'), + max_length=50, + blank=True, + help_text=_('Local facility ID or description') + ) # Generic relations vlan_groups = GenericRelation( @@ -284,7 +290,7 @@ class Location(ContactsMixin, ImageAttachmentsMixin, NestedGroupModel): related_query_name='location' ) - clone_fields = ('site', 'parent', 'status', 'tenant', 'description') + clone_fields = ('site', 'parent', 'status', 'tenant', 'facility', 'description') prerequisite_models = ( 'dcim.Site', ) diff --git a/netbox/dcim/search.py b/netbox/dcim/search.py index 18cf75a9a48..b349bcac063 100644 --- a/netbox/dcim/search.py +++ b/netbox/dcim/search.py @@ -132,10 +132,11 @@ class LocationIndex(SearchIndex): model = models.Location fields = ( ('name', 100), + ('facility', 100), ('slug', 110), ('description', 500), ) - display_attrs = ('site', 'status', 'tenant', 'description') + display_attrs = ('site', 'status', 'tenant', 'facility', 'description') @register_search diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py index a0a71ab3059..e179ec43a87 100644 --- a/netbox/dcim/tables/sites.py +++ b/netbox/dcim/tables/sites.py @@ -152,7 +152,9 @@ class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = Location fields = ( - 'pk', 'id', 'name', 'site', 'status', 'tenant', 'tenant_group', 'rack_count', 'device_count', 'description', - 'slug', 'contacts', 'tags', 'actions', 'created', 'last_updated', + 'pk', 'id', 'name', 'site', 'status', 'facility', 'tenant', 'tenant_group', 'rack_count', 'device_count', + 'description', 'slug', 'contacts', 'tags', 'actions', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'site', 'status', 'facility', 'tenant', 'rack_count', 'device_count', 'description' ) - default_columns = ('pk', 'name', 'site', 'status', 'tenant', 'rack_count', 'device_count', 'description') diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 1e46d66ac78..fffa82a109b 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -359,9 +359,9 @@ def setUpTestData(cls): location.save() locations = ( - Location(name='Location 1A', slug='location-1a', site=sites[0], parent=parent_locations[0], status=LocationStatusChoices.STATUS_PLANNED, description='foobar1'), - Location(name='Location 2A', slug='location-2a', site=sites[1], parent=parent_locations[1], status=LocationStatusChoices.STATUS_STAGING, description='foobar2'), - Location(name='Location 3A', slug='location-3a', site=sites[2], parent=parent_locations[2], status=LocationStatusChoices.STATUS_DECOMMISSIONING, description='foobar3'), + Location(name='Location 1A', slug='location-1a', site=sites[0], parent=parent_locations[0], status=LocationStatusChoices.STATUS_PLANNED, facility='Facility 1', description='foobar1'), + Location(name='Location 2A', slug='location-2a', site=sites[1], parent=parent_locations[1], status=LocationStatusChoices.STATUS_STAGING, facility='Facility 2', description='foobar2'), + Location(name='Location 3A', slug='location-3a', site=sites[2], parent=parent_locations[2], status=LocationStatusChoices.STATUS_DECOMMISSIONING, facility='Facility 3', description='foobar3'), ) for location in locations: location.save() @@ -390,6 +390,10 @@ def test_status(self): params = {'status': [LocationStatusChoices.STATUS_PLANNED, LocationStatusChoices.STATUS_STAGING]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_facility(self): + params = {'facility': ['Facility 1', 'Facility 2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_description(self): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index e9e5a557b26..e3437cefc87 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -213,6 +213,7 @@ def setUpTestData(cls): 'slug': 'location-x', 'site': site.pk, 'status': LocationStatusChoices.STATUS_PLANNED, + 'facility': 'Facility X', 'tenant': tenant.pk, 'description': 'A new location', 'tags': [t.pk for t in tags], diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html index db387b164c1..9f2b766ea61 100644 --- a/netbox/templates/dcim/location.html +++ b/netbox/templates/dcim/location.html @@ -54,6 +54,10 @@