diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d4b7f5d..99f858396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,34 @@ +v2.6.6 (FUTURE) + +## Enhancements + +* [#3259](https://github.com/netbox-community/netbox/issues/3259) - Add `rack` and `site` filters for cables + +--- + v2.6.5 (2019-09-25) ## Enhancements -* [#3297](https://github.com/netbox-community/netbox/issues/3297) - Include reserved units when calculating rack utilization -* [#3347](https://github.com/netbox-community/netbox/issues/3347) - Extend upgrade script to automatically remove stale content types -* [#3352](https://github.com/netbox-community/netbox/issues/3352) - Enable filtering changelog API by `changed_object_id` -* [#3515](https://github.com/netbox-community/netbox/issues/3515) - Enable export templates for inventory items -* [#3524](https://github.com/netbox-community/netbox/issues/3524) - Enable bulk editing of power outlet/power port associations -* [#3529](https://github.com/netbox-community/netbox/issues/3529) - Enable filtering circuits list by region +* [#3297](https://github.com/netbox-community/netbox/issues/3297) - Include reserved units when calculating rack utilization +* [#3347](https://github.com/netbox-community/netbox/issues/3347) - Extend upgrade script to automatically remove stale content types +* [#3352](https://github.com/netbox-community/netbox/issues/3352) - Enable filtering changelog API by `changed_object_id` +* [#3515](https://github.com/netbox-community/netbox/issues/3515) - Enable export templates for inventory items +* [#3524](https://github.com/netbox-community/netbox/issues/3524) - Enable bulk editing of power outlet/power port associations +* [#3529](https://github.com/netbox-community/netbox/issues/3529) - Enable filtering circuits list by region ## Bug Fixes -* [#3435](https://github.com/netbox-community/netbox/issues/3435) - Change IP/prefix CSV export to reference VRF name instead of RD -* [#3464](https://github.com/netbox-community/netbox/issues/3464) - Fix foreground text color on color picker fields -* [#3519](https://github.com/netbox-community/netbox/issues/3519) - Prevent cables from being terminated to virtual/wireless interfaces via API -* [#3521](https://github.com/netbox-community/netbox/issues/3521) - Fix error in `parseURL` related to variables in API URL -* [#3531](https://github.com/netbox-community/netbox/issues/3531) - Fixed rack role foreground color -* [#3534](https://github.com/netbox-community/netbox/issues/3534) - Added blank option for untagged VLANs -* [#3540](https://github.com/netbox-community/netbox/issues/3540) - Fixed virtual machine interface edit with new inline vlan edit fields -* [#3543](https://github.com/netbox-community/netbox/issues/3543) - Added inline VLAN editing to virtual machine interfaces +* [#3435](https://github.com/netbox-community/netbox/issues/3435) - Change IP/prefix CSV export to reference VRF name instead of RD +* [#3464](https://github.com/netbox-community/netbox/issues/3464) - Fix foreground text color on color picker fields +* [#3519](https://github.com/netbox-community/netbox/issues/3519) - Prevent cables from being terminated to virtual/wireless interfaces via API +* [#3521](https://github.com/netbox-community/netbox/issues/3521) - Fix error in `parseURL` related to variables in API URL +* [#3531](https://github.com/netbox-community/netbox/issues/3531) - Fixed rack role foreground color +* [#3534](https://github.com/netbox-community/netbox/issues/3534) - Added blank option for untagged VLANs +* [#3540](https://github.com/netbox-community/netbox/issues/3540) - Fixed virtual machine interface edit with new inline vlan edit fields +* [#3543](https://github.com/netbox-community/netbox/issues/3543) - Added inline VLAN editing to virtual machine interfaces + +--- v2.6.4 (2019-09-19) @@ -39,6 +49,8 @@ v2.6.4 (2019-09-19) * [#3513](https://github.com/netbox-community/netbox/issues/3513) - Fix assignment of tags when creating front/rear ports * [#3514](https://github.com/netbox-community/netbox/issues/3514) - Label TextVar fields when rendering custom script forms +--- + v2.6.3 (2019-09-04) ## New Features diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 0499dcd59..c1274e3d5 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -1,6 +1,5 @@ import django_filters from django.contrib.auth.models import User -from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q from extras.filters import CustomFieldFilterSet, LocalConfigContextFilter @@ -931,13 +930,28 @@ class CableFilter(django_filters.FilterSet): color = django_filters.MultipleChoiceFilter( choices=COLOR_CHOICES ) - device = django_filters.CharFilter( - method='filter_connected_device', - field_name='name' + device_id = MultiValueNumberFilter( + method='filter_device' ) - device_id = django_filters.CharFilter( - method='filter_connected_device', - field_name='pk' + device = MultiValueNumberFilter( + method='filter_device', + field_name='device__name' + ) + rack_id = MultiValueNumberFilter( + method='filter_device', + field_name='device__rack_id' + ) + rack = MultiValueNumberFilter( + method='filter_device', + field_name='device__rack__name' + ) + site_id = MultiValueNumberFilter( + method='filter_device', + field_name='device__site_id' + ) + site = MultiValueNumberFilter( + method='filter_device', + field_name='device__site__slug' ) class Meta: @@ -949,15 +963,12 @@ class CableFilter(django_filters.FilterSet): return queryset return queryset.filter(label__icontains=value) - def filter_connected_device(self, queryset, name, value): - if not value.strip(): - return queryset - try: - device = Device.objects.get(**{name: value}) - except ObjectDoesNotExist: - return queryset.none() - cable_pks = device.get_cables(pk_list=True) - return queryset.filter(pk__in=cable_pks) + def filter_device(self, queryset, name, value): + queryset = queryset.filter( + Q(**{'_termination_a_{}__in'.format(name): value}) | + Q(**{'_termination_b_{}__in'.format(name): value}) + ) + return queryset class ConsoleConnectionFilter(django_filters.FilterSet): diff --git a/netbox/dcim/migrations/0075_cable_devices.py b/netbox/dcim/migrations/0075_cable_devices.py new file mode 100644 index 000000000..d93f1ab7c --- /dev/null +++ b/netbox/dcim/migrations/0075_cable_devices.py @@ -0,0 +1,46 @@ +from django.db import migrations, models +import django.db.models.deletion + + +def cache_cable_devices(apps, schema_editor): + Cable = apps.get_model('dcim', 'Cable') + + # Cache A/B termination devices on all existing Cables. Note that the custom save() method on Cable is not + # available during a migration, so we replicate its logic here. + for cable in Cable.objects.all(): + + termination_a_model = apps.get_model(cable.termination_a_type.app_label, cable.termination_a_type.model) + if hasattr(termination_a_model, 'device'): + termination_a = termination_a_model.objects.get(pk=cable.termination_a_id) + cable._termination_a_device = termination_a.device + + termination_b_model = apps.get_model(cable.termination_b_type.app_label, cable.termination_b_type.model) + if hasattr(termination_b_model, 'device'): + termination_b = termination_b_model.objects.get(pk=cable.termination_b_id) + cable._termination_b_device = termination_b.device + + cable.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0074_increase_field_length_platform_name_slug'), + ] + + operations = [ + migrations.AddField( + model_name='cable', + name='_termination_a_device', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.Device'), + ), + migrations.AddField( + model_name='cable', + name='_termination_b_device', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.Device'), + ), + migrations.RunPython( + code=cache_cable_devices, + reverse_code=migrations.RunPython.noop + ), + ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 5ffd15842..2c554c158 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2781,6 +2781,22 @@ class Cable(ChangeLoggedModel): blank=True, null=True ) + # Cache the associated device (where applicable) for the A and B terminations. This enables filtering of Cables by + # their associated Devices. + _termination_a_device = models.ForeignKey( + to=Device, + on_delete=models.CASCADE, + related_name='+', + blank=True, + null=True + ) + _termination_b_device = models.ForeignKey( + to=Device, + on_delete=models.CASCADE, + related_name='+', + blank=True, + null=True + ) csv_headers = [ 'termination_a_type', 'termination_a_id', 'termination_b_type', 'termination_b_id', 'type', 'status', 'label', @@ -2895,6 +2911,12 @@ class Cable(ChangeLoggedModel): if self.length and self.length_unit: self._abs_length = to_meters(self.length, self.length_unit) + # Store the parent Device for the A and B terminations (if applicable) to enable filtering + if hasattr(self.termination_a, 'device'): + self._termination_a_device = self.termination_a.device + if hasattr(self.termination_b, 'device'): + self._termination_b_device = self.termination_b.device + super().save(*args, **kwargs) def to_csv(self):