1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #4742: Add tagging for cables, power panels, and rack reservations

This commit is contained in:
Jeremy Stretch
2020-06-10 14:55:46 -04:00
parent 67784c0568
commit 88ae522c9a
13 changed files with 86 additions and 12 deletions

View File

@ -8,6 +8,10 @@
NetBox v2.9 replaces Django's built-in permissions framework with one that supports object-based assignment of permissions using arbitrary constraints. When granting a user or group to perform a certain action on one or more types of objects, an administrator can optionally specify a set of constraints. The permission will apply only to objects which match the specified constraints. For example, assigning permission to modify devices with the constraint `{"tenant__group__name": "Customers"}` would grant the permission only for devices assigned to a tenant belonging to the "Customers" group.
### Enhancements
* [#4742](https://github.com/netbox-community/netbox/issues/4742) - Add tagging for cables, power panels, and rack reservations
### Configuration Changes
* `REMOTE_AUTH_DEFAULT_PERMISSIONS` now takes a dictionary rather than a list. This is a mapping of permission names to a dictionary of constraining attributes, or `None`. For example, `['dcim.add_site', 'dcim.change_site']` would become `{'dcim.add_site': None, 'dcim.change_site': None}`.

View File

@ -165,10 +165,11 @@ class RackReservationSerializer(ValidatedModelSerializer):
rack = NestedRackSerializer()
user = NestedUserSerializer()
tenant = NestedTenantSerializer(required=False, allow_null=True)
tags = TagListSerializerField(required=False)
class Meta:
model = RackReservation
fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description']
fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description', 'tags']
class RackElevationDetailFilterSerializer(serializers.Serializer):
@ -640,12 +641,13 @@ class CableSerializer(ValidatedModelSerializer):
termination_b = serializers.SerializerMethodField(read_only=True)
status = ChoiceField(choices=CableStatusChoices, required=False)
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
tags = TagListSerializerField(required=False)
class Meta:
model = Cable
fields = [
'id', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type', 'termination_b_id',
'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit',
'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
]
def _get_termination(self, obj, side):
@ -729,11 +731,12 @@ class PowerPanelSerializer(ValidatedModelSerializer):
allow_null=True,
default=None
)
tags = TagListSerializerField(required=False)
powerfeed_count = serializers.IntegerField(read_only=True)
class Meta:
model = PowerPanel
fields = ['id', 'site', 'rack_group', 'name', 'powerfeed_count']
fields = ['id', 'site', 'rack_group', 'name', 'tags', 'powerfeed_count']
class PowerFeedSerializer(TaggitSerializer, CustomFieldModelSerializer):

View File

@ -298,6 +298,7 @@ class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet):
to_field_name='username',
label='User (name)',
)
tag = TagFilter()
class Meta:
model = RackReservation
@ -1117,6 +1118,7 @@ class CableFilterSet(BaseFilterSet):
method='filter_device',
field_name='device__tenant__slug'
)
tag = TagFilter()
class Meta:
model = Cable
@ -1265,6 +1267,7 @@ class PowerPanelFilterSet(BaseFilterSet):
lookup_expr='in',
label='Rack group (ID)',
)
tag = TagFilter()
class Meta:
model = PowerPanel

View File

@ -750,11 +750,14 @@ class RackReservationForm(BootstrapMixin, TenancyForm, forms.ModelForm):
),
widget=StaticSelect2()
)
tags = TagField(
required=False
)
class Meta:
model = RackReservation
fields = [
'rack', 'units', 'user', 'tenant_group', 'tenant', 'description',
'rack', 'units', 'user', 'tenant_group', 'tenant', 'description', 'tags',
]
def __init__(self, *args, **kwargs):
@ -825,7 +828,7 @@ class RackReservationCSVForm(CSVModelForm):
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
class RackReservationBulkEditForm(BootstrapMixin, BulkEditForm):
class RackReservationBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=RackReservation.objects.all(),
widget=forms.MultipleHiddenInput()
@ -851,6 +854,7 @@ class RackReservationBulkEditForm(BootstrapMixin, BulkEditForm):
class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm):
model = RackReservation
field_order = ['q', 'site', 'group_id', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
@ -872,6 +876,7 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm):
null_option=True,
)
)
tag = TagFilterField(model)
#
@ -3662,11 +3667,14 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
class CableForm(BootstrapMixin, forms.ModelForm):
tags = TagField(
required=False
)
class Meta:
model = Cable
fields = [
'type', 'status', 'label', 'color', 'length', 'length_unit',
'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
]
widgets = {
'status': StaticSelect2,
@ -3799,7 +3807,7 @@ class CableCSVForm(CSVModelForm):
return length_unit if length_unit is not None else ''
class CableBulkEditForm(BootstrapMixin, BulkEditForm):
class CableBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=Cable.objects.all(),
widget=forms.MultipleHiddenInput
@ -3912,6 +3920,7 @@ class CableFilterForm(BootstrapMixin, forms.Form):
required=False,
label='Device'
)
tag = TagFilterField(model)
#
@ -4325,11 +4334,14 @@ class PowerPanelForm(BootstrapMixin, forms.ModelForm):
queryset=RackGroup.objects.all(),
required=False
)
tags = TagField(
required=False
)
class Meta:
model = PowerPanel
fields = [
'site', 'rack_group', 'name',
'site', 'rack_group', 'name', 'tags',
]
@ -4359,7 +4371,7 @@ class PowerPanelCSVForm(CSVModelForm):
self.fields['rack_group'].queryset = self.fields['rack_group'].queryset.filter(**params)
class PowerPanelBulkEditForm(BootstrapMixin, BulkEditForm):
class PowerPanelBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=PowerPanel.objects.all(),
widget=forms.MultipleHiddenInput
@ -4420,6 +4432,7 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldFilterForm):
null_option=True,
)
)
tag = TagFilterField(model)
#

View File

@ -0,0 +1,30 @@
# Generated by Django 3.0.6 on 2020-06-10 18:32
from django.db import migrations
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('extras', '0042_customfield_manager'),
('dcim', '0106_role_default_color'),
]
operations = [
migrations.AddField(
model_name='cable',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AddField(
model_name='powerpanel',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AddField(
model_name='rackreservation',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
]

View File

@ -832,6 +832,7 @@ class RackReservation(ChangeLoggedModel):
description = models.CharField(
max_length=200
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
@ -1832,6 +1833,7 @@ class PowerPanel(ChangeLoggedModel):
name = models.CharField(
max_length=50
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
@ -2106,6 +2108,7 @@ class Cable(ChangeLoggedModel):
blank=True,
null=True
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()

View File

@ -399,6 +399,9 @@ class RackReservationTable(BaseTable):
orderable=False,
verbose_name='Units'
)
tags = TagColumn(
url_name='dcim:rackreservation_list'
)
actions = tables.TemplateColumn(
template_code=RACKRESERVATION_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}},
@ -408,7 +411,8 @@ class RackReservationTable(BaseTable):
class Meta(BaseTable.Meta):
model = RackReservation
fields = (
'pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions',
'pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'tags',
'actions',
)
default_columns = (
'pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description', 'actions',
@ -1086,12 +1090,15 @@ class CableTable(BaseTable):
order_by='_abs_length'
)
color = ColorColumn()
tags = TagColumn(
url_name='dcim:cable_list'
)
class Meta(BaseTable.Meta):
model = Cable
fields = (
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
'status', 'type', 'color', 'length',
'status', 'type', 'color', 'length', 'tags',
)
default_columns = (
'pk', 'id', 'label', 'termination_a_parent', 'termination_a', 'termination_b_parent', 'termination_b',
@ -1245,10 +1252,13 @@ class PowerPanelTable(BaseTable):
template_code=POWERPANEL_POWERFEED_COUNT,
verbose_name='Feeds'
)
tags = TagColumn(
url_name='dcim:powerpanel_list'
)
class Meta(BaseTable.Meta):
model = PowerPanel
fields = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count')
fields = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count', 'tags')
default_columns = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count')

View File

@ -202,6 +202,7 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
'user': user3.pk,
'tenant': None,
'description': 'Rack reservation',
'tags': 'Alpha,Bravo,Charlie',
}
cls.csv_data = (
@ -1510,6 +1511,7 @@ class CableTestCase(
'color': 'c0c0c0',
'length': 100,
'length_unit': CableLengthUnitChoices.UNIT_FOOT,
'tags': 'Alpha,Bravo,Charlie',
}
cls.csv_data = (
@ -1609,6 +1611,7 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
'site': sites[1].pk,
'rack_group': rackgroups[1].pk,
'name': 'Power Panel X',
'tags': 'Alpha,Bravo,Charlie',
}
cls.csv_data = (

View File

@ -81,6 +81,7 @@
</tr>
</table>
</div>
{% include 'extras/inc/tags_panel.html' with tags=cable.tags.all url='dcim:cable_list' %}
{% plugin_left_page cable %}
</div>
<div class="col-md-6">

View File

@ -29,5 +29,6 @@
{% endif %}
</div>
</div>
{% render_field form.tags %}
</div>
</div>

View File

@ -82,6 +82,7 @@
</tr>
</table>
</div>
{% include 'extras/inc/tags_panel.html' with tags=powerpanel.tags.all url='dcim:powerpanel_list' %}
{% plugin_left_page powerpanel %}
</div>
<div class="col-md-9">

View File

@ -124,6 +124,7 @@
</tr>
</table>
</div>
{% include 'extras/inc/tags_panel.html' with tags=rackreservation.tags.all url='dcim:rackreservation_list' %}
{% plugin_left_page rackreservation %}
</div>
<div class="col-md-6">

View File

@ -16,6 +16,7 @@
{% render_field form.tenant_group %}
{% render_field form.tenant %}
{% render_field form.description %}
{% render_field form.tags %}
</div>
</div>
{% endblock %}