From faa22cb6377e2a2b2c02a773c5a839b04d896764 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Sat, 15 Feb 2020 22:39:08 +0000 Subject: [PATCH 01/45] Fixes #2511: Compare object change to the previous change --- netbox/extras/views.py | 24 +++++++++++++++++++ netbox/templates/extras/objectchange.html | 29 +++++++++++++++++++++++ netbox/utilities/utils.py | 16 +++++++++++++ 3 files changed, 69 insertions(+) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 3912c602f..b625cf7b1 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -12,6 +12,7 @@ from django_tables2 import RequestConfig from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator +from utilities.utils import shallow_compare_dict from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView from . import filters, forms from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem @@ -207,8 +208,31 @@ class ObjectChangeView(PermissionRequiredMixin, View): orderable=False ) + objectchanges = ObjectChange.objects.filter( + changed_object_type=objectchange.changed_object_type, + changed_object_id=objectchange.changed_object_id, + ) + + next_change = objectchanges.filter(time__gt=objectchange.time).order_by('time').first() + prev_change = objectchanges.filter(time__lt=objectchange.time).order_by('-time').first() + + if prev_change: + diff_added = shallow_compare_dict( + prev_change.object_data, + objectchange.object_data, + exclude=['last_updated'], + ) + diff_removed = {x: prev_change.object_data.get(x) for x in diff_added} + else: + # No previous change; this is the initial change that added the object + diff_added = diff_removed = objectchange.object_data + return render(request, 'extras/objectchange.html', { 'objectchange': objectchange, + 'diff_added': diff_added, + 'diff_removed': diff_removed, + 'next_change': next_change, + 'prev_change': prev_change, 'related_changes_table': related_changes_table, 'related_changes_count': related_changes.count() }) diff --git a/netbox/templates/extras/objectchange.html b/netbox/templates/extras/objectchange.html index ee29281f9..35be29bbd 100644 --- a/netbox/templates/extras/objectchange.html +++ b/netbox/templates/extras/objectchange.html @@ -83,6 +83,35 @@ +
+
+ Difference + +
+
+ {% if diff_added == diff_removed %} + + {% if objectchange.action == 'create' %} + Object created + {% elif objectchange.action == 'delete' %} + Object deleted + {% else %} + No changes + {% endif %} + + {% else %} +
{{ diff_removed|render_json }}
+
{{ diff_added|render_json }}
+ {% endif %} +
+
diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index 979f95af9..6969a60e9 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -222,3 +222,19 @@ def querydict_to_dict(querydict): key: querydict.get(key) if len(value) == 1 and key != 'pk' else querydict.getlist(key) for key, value in querydict.lists() } + + +def shallow_compare_dict(source_dict, destination_dict, exclude=None): + """ + Return a new dictionary of the different keys. The values of `destination_dict` are returned. Only the equality of + the first layer of keys/values is checked. `exclude` is a list or tuple of keys to be ignored. + """ + difference = {} + + for key in destination_dict: + if source_dict.get(key) != destination_dict[key]: + if isinstance(exclude, (list, tuple)) and key in exclude: + continue + difference[key] = destination_dict[key] + + return difference From 89ab6553d63dbdbbf23866d67deb1ec485b389a4 Mon Sep 17 00:00:00 2001 From: Saria Hajjar Date: Sat, 15 Feb 2020 23:55:03 +0000 Subject: [PATCH 02/45] Changelog for #2511 --- docs/release-notes/version-2.7.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index e224196ad..39f3a0ac2 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -2,6 +2,7 @@ ## Enhancements +* [#2511](https://github.com/netbox-community/netbox/issues/2511) - Compare object change to the previous change * [#3840](https://github.com/netbox-community/netbox/issues/3840) - Enhance search function when selecting VLANs for interface assignment * [#4170](https://github.com/netbox-community/netbox/issues/4170) - Improve color contrast in rack elevation drawings From 5b505b21c8bf6a71669b3dca409c36f0b3724441 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 10:50:14 -0500 Subject: [PATCH 03/45] Fixes #4183: Fix representation of NaturalOrderingField values in change log --- docs/release-notes/version-2.7.md | 1 + netbox/utilities/fields.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index e224196ad..f59aa72e4 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -10,6 +10,7 @@ * [#2519](https://github.com/netbox-community/netbox/issues/2519) - Avoid race condition when provisioning "next available" IPs/prefixes via the API * [#4168](https://github.com/netbox-community/netbox/issues/4168) - Role is not required when creating a virtual machine * [#4175](https://github.com/netbox-community/netbox/issues/4175) - Fix potential exception when bulk editing objects from a filtered list +* [#4183](https://github.com/netbox-community/netbox/issues/4183) - Fix representation of NaturalOrderingField values in change log --- diff --git a/netbox/utilities/fields.py b/netbox/utilities/fields.py index 6181a7ca1..4eb19f539 100644 --- a/netbox/utilities/fields.py +++ b/netbox/utilities/fields.py @@ -56,8 +56,11 @@ class NaturalOrderingField(models.CharField): """ Generate a naturalized value from the target field """ - value = getattr(model_instance, self.target_field) - return self.naturalize_function(value, max_length=self.max_length) + original_value = getattr(model_instance, self.target_field) + naturalized_value = self.naturalize_function(original_value, max_length=self.max_length) + setattr(model_instance, self.attname, naturalized_value) + + return naturalized_value def deconstruct(self): kwargs = super().deconstruct()[3] # Pass kwargs from CharField From a456cbb26ce93507f810514ef7c60ae3971e8933 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 11:08:16 -0500 Subject: [PATCH 04/45] Fixes #4179: Site is required when creating a rack group or power panel --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/forms.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index f59aa72e4..3d4306ac7 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -10,6 +10,7 @@ * [#2519](https://github.com/netbox-community/netbox/issues/2519) - Avoid race condition when provisioning "next available" IPs/prefixes via the API * [#4168](https://github.com/netbox-community/netbox/issues/4168) - Role is not required when creating a virtual machine * [#4175](https://github.com/netbox-community/netbox/issues/4175) - Fix potential exception when bulk editing objects from a filtered list +* [#4179](https://github.com/netbox-community/netbox/issues/4179) - Site is required when creating a rack group or power panel * [#4183](https://github.com/netbox-community/netbox/issues/4183) - Fix representation of NaturalOrderingField values in change log --- diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 4c8a0821f..c4b5d4503 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -385,7 +385,6 @@ class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): class RackGroupForm(BootstrapMixin, forms.ModelForm): site = DynamicModelChoiceField( queryset=Site.objects.all(), - required=False, widget=APISelect( api_url="/api/dcim/sites/" ) @@ -4522,7 +4521,6 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm): class PowerPanelForm(BootstrapMixin, forms.ModelForm): site = DynamicModelChoiceField( queryset=Site.objects.all(), - required=False, widget=APISelect( api_url="/api/dcim/sites/", filter_for={ From 4ea8967c2d43e1c49985b2c32e28ad8d2cd34809 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 11:14:37 -0500 Subject: [PATCH 05/45] Fixes #4194: Role field should not be required when searching/filtering secrets --- docs/release-notes/version-2.7.md | 1 + netbox/secrets/forms.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 3d4306ac7..c6985f9f2 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -12,6 +12,7 @@ * [#4175](https://github.com/netbox-community/netbox/issues/4175) - Fix potential exception when bulk editing objects from a filtered list * [#4179](https://github.com/netbox-community/netbox/issues/4179) - Site is required when creating a rack group or power panel * [#4183](https://github.com/netbox-community/netbox/issues/4183) - Fix representation of NaturalOrderingField values in change log +* [#4194](https://github.com/netbox-community/netbox/issues/4194) - Role field should not be required when searching/filtering secrets --- diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py index 79064e0dd..88e5325ec 100644 --- a/netbox/secrets/forms.py +++ b/netbox/secrets/forms.py @@ -185,7 +185,7 @@ class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm): role = DynamicModelMultipleChoiceField( queryset=SecretRole.objects.all(), to_field_name='slug', - required=True, + required=False, widget=APISelectMultiple( api_url="/api/secrets/secret-roles/", value_field="slug", From 2a1de0202ffaea722cad5e9d65b1572b466dd9f9 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 11:43:47 -0500 Subject: [PATCH 06/45] Add helpful links to "new issue" page --- .github/ISSUE_TEMPLATE/config.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ab7d7cdc4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,9 @@ +# Reference: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +blank_issues_enabled: false +contact_links: + - name: ๐Ÿ“– Contributing Policy + url: https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md + about: Please read through our contributing policy before opening an issue or pull request + - name: ๐Ÿ’ฌ Discussion Group + url: https://groups.google.com/forum/#!forum/netbox-discuss + about: Join our discussion group for assistance with installation issues and other problems From 84d078a5390befbe7f628c92b860bf43fd907911 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 16:21:50 -0500 Subject: [PATCH 07/45] Fixes #4196: Fix exception when viewing LLDP neighbors page --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/views.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index c6985f9f2..458328369 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -13,6 +13,7 @@ * [#4179](https://github.com/netbox-community/netbox/issues/4179) - Site is required when creating a rack group or power panel * [#4183](https://github.com/netbox-community/netbox/issues/4183) - Fix representation of NaturalOrderingField values in change log * [#4194](https://github.com/netbox-community/netbox/issues/4194) - Role field should not be required when searching/filtering secrets +* [#4196](https://github.com/netbox-community/netbox/issues/4196) - Fix exception when viewing LLDP neighbors page --- diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 0bb6658a2..91b32bc70 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -31,6 +31,7 @@ from utilities.views import ( from virtualization.models import VirtualMachine from . import filters, forms, tables from .choices import DeviceFaceChoices +from .constants import NONCONNECTABLE_IFACE_TYPES from .models import ( Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, @@ -1181,7 +1182,7 @@ class DeviceLLDPNeighborsView(PermissionRequiredMixin, View): def get(self, request, pk): device = get_object_or_404(Device, pk=pk) - interfaces = device.vc_interfaces.connectable().prefetch_related( + interfaces = device.vc_interfaces.exclude(type__in=NONCONNECTABLE_IFACE_TYPES).prefetch_related( '_connected_interface__device' ) From ae1767b5d03002ae56316bfaa166055c0d645c28 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 16:22:17 -0500 Subject: [PATCH 08/45] Remove obsolete InterfaceManager --- netbox/dcim/managers.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 netbox/dcim/managers.py diff --git a/netbox/dcim/managers.py b/netbox/dcim/managers.py deleted file mode 100644 index 502719646..000000000 --- a/netbox/dcim/managers.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.db.models import Manager, QuerySet - -from .constants import NONCONNECTABLE_IFACE_TYPES - - -class InterfaceQuerySet(QuerySet): - - def connectable(self): - """ - Return only physical interfaces which are capable of being connected to other interfaces (i.e. not virtual or - wireless). - """ - return self.exclude(type__in=NONCONNECTABLE_IFACE_TYPES) - - -class InterfaceManager(Manager): - - def get_queryset(self): - return InterfaceQuerySet(self.model, using=self._db) From 8cfb5ac5c627504cf4199d77633cea66cb17457e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Feb 2020 16:56:50 -0500 Subject: [PATCH 09/45] Fixes #3967: Fix missing migration for interface templates of type "other" --- docs/release-notes/version-2.7.md | 1 + .../0097_interfacetemplate_type_other.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 netbox/dcim/migrations/0097_interfacetemplate_type_other.py diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 458328369..02b3aa9bf 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -8,6 +8,7 @@ ## Bug Fixes * [#2519](https://github.com/netbox-community/netbox/issues/2519) - Avoid race condition when provisioning "next available" IPs/prefixes via the API +* [#3967](https://github.com/netbox-community/netbox/issues/3967) - Fix missing migration for interface templates of type "other" * [#4168](https://github.com/netbox-community/netbox/issues/4168) - Role is not required when creating a virtual machine * [#4175](https://github.com/netbox-community/netbox/issues/4175) - Fix potential exception when bulk editing objects from a filtered list * [#4179](https://github.com/netbox-community/netbox/issues/4179) - Site is required when creating a rack group or power panel diff --git a/netbox/dcim/migrations/0097_interfacetemplate_type_other.py b/netbox/dcim/migrations/0097_interfacetemplate_type_other.py new file mode 100644 index 000000000..d71b5c655 --- /dev/null +++ b/netbox/dcim/migrations/0097_interfacetemplate_type_other.py @@ -0,0 +1,20 @@ +from django.db import migrations + + +def interfacetemplate_type_to_slug(apps, schema_editor): + InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate') + InterfaceTemplate.objects.filter(type=32767).update(type='other') + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0096_interface_ordering'), + ] + + operations = [ + # Missed type "other" in the initial migration (see #3967) + migrations.RunPython( + code=interfacetemplate_type_to_slug + ), + ] From 58716407013d17bba7527a708e6eea41c05bdb9b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Feb 2020 10:31:10 -0500 Subject: [PATCH 10/45] Closes #4199: Update example report to use ChoiceSet --- docs/additional-features/reports.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/additional-features/reports.md b/docs/additional-features/reports.md index fc1e89221..6deddc140 100644 --- a/docs/additional-features/reports.md +++ b/docs/additional-features/reports.md @@ -32,7 +32,8 @@ class DeviceIPsReport(Report): Within each report class, we'll create a number of test methods to execute our report's logic. In DeviceConnectionsReport, for instance, we want to ensure that every live device has a console connection, an out-of-band management connection, and two power connections. ``` -from dcim.constants import CONNECTION_STATUS_PLANNED, DEVICE_STATUS_ACTIVE +from dcim.choices import DeviceStatusChoices +from dcim.constants import CONNECTION_STATUS_PLANNED from dcim.models import ConsolePort, Device, PowerPort from extras.reports import Report @@ -43,7 +44,8 @@ class DeviceConnectionsReport(Report): def test_console_connection(self): # Check that every console port for every active device has a connection defined. - for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=DEVICE_STATUS_ACTIVE): + active = DeviceStatusChoices.STATUS_ACTIVE + for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=active): if console_port.connected_endpoint is None: self.log_failure( console_port.device, @@ -60,7 +62,7 @@ class DeviceConnectionsReport(Report): def test_power_connections(self): # Check that every active device has at least two connected power supplies. - for device in Device.objects.filter(status=DEVICE_STATUS_ACTIVE): + for device in Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE): connected_ports = 0 for power_port in PowerPort.objects.filter(device=device): if power_port.connected_endpoint is not None: From 626513a8b20d07e6339605e2652aceaae29b8e7f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Feb 2020 11:29:42 -0500 Subject: [PATCH 11/45] Fixes #4202: Prevent reassignment to master device when bulk editing VC member interfaces --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/forms.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 02b3aa9bf..c9bfd2cae 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -15,6 +15,7 @@ * [#4183](https://github.com/netbox-community/netbox/issues/4183) - Fix representation of NaturalOrderingField values in change log * [#4194](https://github.com/netbox-community/netbox/issues/4194) - Role field should not be required when searching/filtering secrets * [#4196](https://github.com/netbox-community/netbox/issues/4196) - Fix exception when viewing LLDP neighbors page +* [#4202](https://github.com/netbox-community/netbox/issues/4202) - Prevent reassignment to master device when bulk editing VC member interfaces --- diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index c4b5d4503..9ec2c443e 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -2763,6 +2763,7 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): device = forms.ModelChoiceField( queryset=Device.objects.all(), required=False, + disabled=True, widget=forms.HiddenInput() ) type = forms.ChoiceField( @@ -3060,6 +3061,7 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): device = forms.ModelChoiceField( queryset=Device.objects.all(), required=False, + disabled=True, widget=forms.HiddenInput() ) type = forms.ChoiceField( From 2cf990bd9247a5977602601e7bf1d2db08449694 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Feb 2020 12:45:52 -0500 Subject: [PATCH 12/45] Standardize on two-word form of "change log" --- netbox/circuits/tables.py | 2 +- netbox/dcim/tables.py | 16 ++++++++-------- netbox/extras/tables.py | 2 +- netbox/ipam/tables.py | 6 +++--- netbox/secrets/tables.py | 2 +- netbox/templates/circuits/circuit.html | 2 +- netbox/templates/circuits/provider.html | 2 +- netbox/templates/dcim/cable.html | 2 +- netbox/templates/dcim/device.html | 2 +- netbox/templates/dcim/devicetype.html | 2 +- netbox/templates/dcim/interface.html | 2 +- netbox/templates/dcim/powerfeed.html | 2 +- netbox/templates/dcim/powerpanel.html | 2 +- netbox/templates/dcim/rack.html | 2 +- netbox/templates/dcim/site.html | 2 +- netbox/templates/extras/object_changelog.html | 4 ++-- netbox/templates/extras/objectchange.html | 2 +- netbox/templates/extras/tag.html | 2 +- netbox/templates/home.html | 2 +- netbox/templates/ipam/aggregate.html | 2 +- netbox/templates/ipam/inc/service.html | 2 +- netbox/templates/ipam/ipaddress.html | 2 +- netbox/templates/ipam/prefix.html | 2 +- netbox/templates/ipam/vlan.html | 2 +- netbox/templates/ipam/vrf.html | 2 +- netbox/templates/secrets/secret.html | 2 +- netbox/templates/tenancy/tenant.html | 2 +- netbox/templates/virtualization/cluster.html | 2 +- .../templates/virtualization/virtualmachine.html | 2 +- netbox/tenancy/tables.py | 2 +- netbox/virtualization/tables.py | 4 ++-- 31 files changed, 42 insertions(+), 42 deletions(-) diff --git a/netbox/circuits/tables.py b/netbox/circuits/tables.py index dbd9e6ba1..a425b3ace 100644 --- a/netbox/circuits/tables.py +++ b/netbox/circuits/tables.py @@ -6,7 +6,7 @@ from utilities.tables import BaseTable, ToggleColumn from .models import Circuit, CircuitType, Provider CIRCUITTYPE_ACTIONS = """ - + {% if perms.circuit.change_circuittype %} diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 1f67b93f1..1cc438f38 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -41,7 +41,7 @@ DEVICE_LINK = """ """ REGION_ACTIONS = """ - + {% if perms.dcim.change_region %} @@ -50,7 +50,7 @@ REGION_ACTIONS = """ """ RACKGROUP_ACTIONS = """ - + @@ -64,7 +64,7 @@ RACKGROUP_ACTIONS = """ """ RACKROLE_ACTIONS = """ - + {% if perms.dcim.change_rackrole %} @@ -86,7 +86,7 @@ RACK_DEVICE_COUNT = """ """ RACKRESERVATION_ACTIONS = """ - + {% if perms.dcim.change_rackreservation %} @@ -95,7 +95,7 @@ RACKRESERVATION_ACTIONS = """ """ MANUFACTURER_ACTIONS = """ - + {% if perms.dcim.change_manufacturer %} @@ -104,7 +104,7 @@ MANUFACTURER_ACTIONS = """ """ DEVICEROLE_ACTIONS = """ - + {% if perms.dcim.change_devicerole %} @@ -129,7 +129,7 @@ PLATFORM_VM_COUNT = """ """ PLATFORM_ACTIONS = """ - + {% if perms.dcim.change_platform %} @@ -166,7 +166,7 @@ UTILIZATION_GRAPH = """ """ VIRTUALCHASSIS_ACTIONS = """ - + {% if perms.dcim.change_virtualchassis %} diff --git a/netbox/extras/tables.py b/netbox/extras/tables.py index dca84dfdc..08c5ed471 100644 --- a/netbox/extras/tables.py +++ b/netbox/extras/tables.py @@ -5,7 +5,7 @@ from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn from .models import ConfigContext, ObjectChange, Tag, TaggedItem TAG_ACTIONS = """ - + {% if perms.taggit.change_tag %} diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index ad119a907..8f059c652 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -26,7 +26,7 @@ RIR_UTILIZATION = """ """ RIR_ACTIONS = """ - + {% if perms.ipam.change_rir %} @@ -48,7 +48,7 @@ ROLE_VLAN_COUNT = """ """ ROLE_ACTIONS = """ - + {% if perms.ipam.change_role %} @@ -145,7 +145,7 @@ VLAN_ROLE_LINK = """ """ VLANGROUP_ACTIONS = """ - + {% with next_vid=record.get_next_available_vid %} diff --git a/netbox/secrets/tables.py b/netbox/secrets/tables.py index cc1760b93..1e8a4e669 100644 --- a/netbox/secrets/tables.py +++ b/netbox/secrets/tables.py @@ -4,7 +4,7 @@ from utilities.tables import BaseTable, ToggleColumn from .models import SecretRole, Secret SECRETROLE_ACTIONS = """ - + {% if perms.secrets.change_secretrole %} diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html index 712ee5861..07d3c360d 100644 --- a/netbox/templates/circuits/circuit.html +++ b/netbox/templates/circuits/circuit.html @@ -49,7 +49,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/circuits/provider.html b/netbox/templates/circuits/provider.html index 5bf05b0e2..e99f8b2c4 100644 --- a/netbox/templates/circuits/provider.html +++ b/netbox/templates/circuits/provider.html @@ -54,7 +54,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/cable.html b/netbox/templates/dcim/cable.html index a190a7001..a78879b23 100644 --- a/netbox/templates/dcim/cable.html +++ b/netbox/templates/dcim/cable.html @@ -31,7 +31,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 5ede19d78..8c457121f 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -119,7 +119,7 @@ {% endif %} {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 68384f12b..99f76ab35 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -54,7 +54,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index 6027afb58..4fc7d21ac 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -34,7 +34,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/powerfeed.html b/netbox/templates/dcim/powerfeed.html index 3ce1d36f2..1f6c6ab8e 100644 --- a/netbox/templates/dcim/powerfeed.html +++ b/netbox/templates/dcim/powerfeed.html @@ -52,7 +52,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/powerpanel.html b/netbox/templates/dcim/powerpanel.html index b0a6961f6..6d47e08b1 100644 --- a/netbox/templates/dcim/powerpanel.html +++ b/netbox/templates/dcim/powerpanel.html @@ -48,7 +48,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 51faeef50..067f4fbdb 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -53,7 +53,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index decfc9261..dfbf65aeb 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -60,7 +60,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/extras/object_changelog.html b/netbox/templates/extras/object_changelog.html index 5d649a692..970b54d4d 100644 --- a/netbox/templates/extras/object_changelog.html +++ b/netbox/templates/extras/object_changelog.html @@ -1,12 +1,12 @@ {% extends base_template %} -{% block title %}{% if obj %}{{ obj }}{% else %}{{ block.super }}{% endif %} - Changelog{% endblock %} +{% block title %}{% if obj %}{{ obj }}{% else %}{{ block.super }}{% endif %} - Change Log{% endblock %} {% block content %} {% if obj %}

{{ obj }}

{% endif %} {% include 'panel_table.html' %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
- Changelog retention: {% if settings.CHANGELOG_RETENTION %}{{ settings.CHANGELOG_RETENTION }} days{% else %}Indefinite{% endif %} + Change log retention: {% if settings.CHANGELOG_RETENTION %}{{ settings.CHANGELOG_RETENTION }} days{% else %}Indefinite{% endif %}
{% endblock %} diff --git a/netbox/templates/extras/objectchange.html b/netbox/templates/extras/objectchange.html index 35be29bbd..16efa6421 100644 --- a/netbox/templates/extras/objectchange.html +++ b/netbox/templates/extras/objectchange.html @@ -7,7 +7,7 @@
- Changelog + Change Log
{% if changelog and perms.extras.view_objectchange %}
diff --git a/netbox/templates/ipam/aggregate.html b/netbox/templates/ipam/aggregate.html index 66281aace..590ed9208 100644 --- a/netbox/templates/ipam/aggregate.html +++ b/netbox/templates/ipam/aggregate.html @@ -48,7 +48,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/ipam/inc/service.html b/netbox/templates/ipam/inc/service.html index 6a3a23bde..9611be175 100644 --- a/netbox/templates/ipam/inc/service.html +++ b/netbox/templates/ipam/inc/service.html @@ -14,7 +14,7 @@ {{ service.description }} - + {% if perms.ipam.change_service %} diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index 50bd90610..08a311492 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -49,7 +49,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/ipam/prefix.html b/netbox/templates/ipam/prefix.html index 324bd927d..7bfa7ba1f 100644 --- a/netbox/templates/ipam/prefix.html +++ b/netbox/templates/ipam/prefix.html @@ -69,7 +69,7 @@ {% endif %} {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/ipam/vlan.html b/netbox/templates/ipam/vlan.html index 50b964992..246f3c866 100644 --- a/netbox/templates/ipam/vlan.html +++ b/netbox/templates/ipam/vlan.html @@ -55,7 +55,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/ipam/vrf.html b/netbox/templates/ipam/vrf.html index 242cfde92..7bb2dea25 100644 --- a/netbox/templates/ipam/vrf.html +++ b/netbox/templates/ipam/vrf.html @@ -46,7 +46,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/secrets/secret.html b/netbox/templates/secrets/secret.html index 438a5a943..6045897c9 100644 --- a/netbox/templates/secrets/secret.html +++ b/netbox/templates/secrets/secret.html @@ -34,7 +34,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/tenancy/tenant.html b/netbox/templates/tenancy/tenant.html index 71c063502..9a1ed1be8 100644 --- a/netbox/templates/tenancy/tenant.html +++ b/netbox/templates/tenancy/tenant.html @@ -49,7 +49,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/virtualization/cluster.html b/netbox/templates/virtualization/cluster.html index 6b90a660d..92540fd70 100644 --- a/netbox/templates/virtualization/cluster.html +++ b/netbox/templates/virtualization/cluster.html @@ -49,7 +49,7 @@ {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html index 10c1f36d4..8f3bb61d4 100644 --- a/netbox/templates/virtualization/virtualmachine.html +++ b/netbox/templates/virtualization/virtualmachine.html @@ -54,7 +54,7 @@ {% endif %} {% if perms.extras.view_objectchange %} {% endif %} diff --git a/netbox/tenancy/tables.py b/netbox/tenancy/tables.py index 884bdc3df..af4fb34c0 100644 --- a/netbox/tenancy/tables.py +++ b/netbox/tenancy/tables.py @@ -4,7 +4,7 @@ from utilities.tables import BaseTable, ToggleColumn from .models import Tenant, TenantGroup TENANTGROUP_ACTIONS = """ - + {% if perms.tenancy.change_tenantgroup %} diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py index ba4554ff5..fdb997dab 100644 --- a/netbox/virtualization/tables.py +++ b/netbox/virtualization/tables.py @@ -7,7 +7,7 @@ from utilities.tables import BaseTable, ToggleColumn from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine CLUSTERTYPE_ACTIONS = """ - + {% if perms.virtualization.change_clustertype %} @@ -16,7 +16,7 @@ CLUSTERTYPE_ACTIONS = """ """ CLUSTERGROUP_ACTIONS = """ - + {% if perms.virtualization.change_clustergroup %} From f05c7be394e01a8c23e70b9d817ffb5774f8bece Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Feb 2020 13:28:07 -0500 Subject: [PATCH 13/45] Fixes #4204: Fix assignment of mask length when bulk editing prefixes --- docs/release-notes/version-2.7.md | 1 + netbox/utilities/views.py | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 3a4e466e3..14fe69622 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -17,6 +17,7 @@ * [#4194](https://github.com/netbox-community/netbox/issues/4194) - Role field should not be required when searching/filtering secrets * [#4196](https://github.com/netbox-community/netbox/issues/4196) - Fix exception when viewing LLDP neighbors page * [#4202](https://github.com/netbox-community/netbox/issues/4202) - Prevent reassignment to master device when bulk editing VC member interfaces +* [#4204](https://github.com/netbox-community/netbox/issues/4204) - Fix assignment of mask length when bulk editing prefixes --- diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index d0257324e..19a6d655e 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -656,9 +656,8 @@ class BulkEditView(GetReturnURLMixin, View): try: model_field = model._meta.get_field(name) except FieldDoesNotExist: - # The form field is used to modify a field rather than set its value directly, - # so we skip it. - continue + # This form field is used to modify a field rather than set its value directly + model_field = None # Handle nullification if name in form.nullable_fields and name in nullified_fields: From 7a53e24f9721a8506008e8bafc25ddd04fa2f412 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Feb 2020 13:53:11 -0500 Subject: [PATCH 14/45] Closes #3810: Preserve slug value when editing existing objects --- docs/release-notes/version-2.7.md | 1 + netbox/project-static/js/forms.js | 14 ++++++++++---- netbox/utilities/forms.py | 10 +++++++++- netbox/utilities/templates/widgets/sluginput.html | 8 ++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 netbox/utilities/templates/widgets/sluginput.html diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 14fe69622..ef05bda8c 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -3,6 +3,7 @@ ## Enhancements * [#2511](https://github.com/netbox-community/netbox/issues/2511) - Compare object change to the previous change +* [#3810](https://github.com/netbox-community/netbox/issues/3810) - Preserve slug value when editing existing objects * [#3840](https://github.com/netbox-community/netbox/issues/3840) - Enhance search function when selecting VLANs for interface assignment * [#4170](https://github.com/netbox-community/netbox/issues/4170) - Improve color contrast in rack elevation drawings diff --git a/netbox/project-static/js/forms.js b/netbox/project-static/js/forms.js index 802d1b4e9..7f71d28c0 100644 --- a/netbox/project-static/js/forms.js +++ b/netbox/project-static/js/forms.js @@ -42,17 +42,23 @@ $(document).ready(function() { return s.substring(0, num_chars); // Trim to first num_chars chars } var slug_field = $('#id_slug'); - slug_field.change(function() { - $(this).attr('_changed', true); - }); if (slug_field) { var slug_source = $('#id_' + slug_field.attr('slug-source')); var slug_length = slug_field.attr('maxlength'); + if (slug_field[0].value) { + slug_field.attr('_changed', true); + } + slug_field.change(function() { + $(this).attr('_changed', true); + }); slug_source.on('keyup change', function() { if (slug_field && !slug_field.attr('_changed')) { slug_field.val(slugify($(this).val(), (slug_length ? slug_length : 50))); } - }) + }); + $('button.reslugify').click(function() { + slug_field.val(slugify(slug_source.val(), (slug_length ? slug_length : 50))); + }); } // Bulk edit nullification diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index c629d70a2..a7ee63eaa 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -132,6 +132,13 @@ class SmallTextarea(forms.Textarea): pass +class SlugWidget(forms.TextInput): + """ + Subclass TextInput and add a slug regeneration button next to the form field. + """ + template_name = 'widgets/sluginput.html' + + class ColorSelect(forms.Select): """ Extends the built-in Select widget to colorize each
+
+
Rack Images
+
+ {% render_field form.front_image %} + {% render_field form.rear_image %} +
+
{% if form.custom_fields %}
Custom Fields
diff --git a/netbox/templates/dcim/inc/rack_elevation.html b/netbox/templates/dcim/inc/rack_elevation.html index b0fcf4908..feced6a22 100644 --- a/netbox/templates/dcim/inc/rack_elevation.html +++ b/netbox/templates/dcim/inc/rack_elevation.html @@ -1,7 +1,4 @@ {% load helpers %} -
- - - +
diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 067f4fbdb..a43b00f54 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -47,6 +47,11 @@
{% custom_links rack %}
+
+ +
+ {% include 'inc/custom_fields_panel.html' with obj=powerfeed %} + {% include 'extras/inc/tags_panel.html' with tags=powerfeed.tags.all url='dcim:powerfeed_list' %}
Comments From ba6562a5dbe1caeeb609ad63dfbbad6467f014db Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 13:09:43 -0500 Subject: [PATCH 25/45] Add ability to toggle the inclusion of device images when rendering a rack elevation SVG --- netbox/dcim/api/serializers.py | 7 +++++++ netbox/dcim/api/views.py | 8 +++++++- netbox/dcim/elevations.py | 19 +++++++++++-------- netbox/dcim/models/__init__.py | 9 +++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 234a9fb1c..3859ecfeb 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -186,6 +186,9 @@ class RackElevationDetailFilterSerializer(serializers.Serializer): unit_height = serializers.IntegerField( default=RACK_ELEVATION_UNIT_HEIGHT_DEFAULT ) + legend_width = serializers.IntegerField( + default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT + ) exclude = serializers.IntegerField( required=False, default=None @@ -194,6 +197,10 @@ class RackElevationDetailFilterSerializer(serializers.Serializer): required=False, default=True ) + include_images = serializers.BooleanField( + required=False, + default=True + ) # diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 8bb127f67..f8297fe46 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -220,7 +220,13 @@ class RackViewSet(CustomFieldModelViewSet): if data['render'] == 'svg': # Render and return the elevation as an SVG drawing with the correct content type - drawing = rack.get_elevation_svg(data['face'], data['unit_width'], data['unit_height']) + drawing = rack.get_elevation_svg( + face=data['face'], + unit_width=data['unit_width'], + unit_height=data['unit_height'], + legend_width=data['legend_width'], + include_images=data['include_images'] + ) return HttpResponse(drawing.tostring(), content_type='image/svg+xml') else: diff --git a/netbox/dcim/elevations.py b/netbox/dcim/elevations.py index e8b476b60..9c58134fd 100644 --- a/netbox/dcim/elevations.py +++ b/netbox/dcim/elevations.py @@ -11,9 +11,13 @@ from .choices import DeviceFaceChoices class RackElevationSVG: """ Use this class to render a rack elevation as an SVG image. + + :param rack: A NetBox Rack instance + :param include_images: If true, the SVG document will embed front/rear device face images, where available """ - def __init__(self, rack): + def __init__(self, rack, include_images=True): self.rack = rack + self.include_images = include_images @staticmethod def _add_gradient(drawing, id_, color): @@ -46,8 +50,7 @@ class RackElevationSVG: return drawing - @staticmethod - def _draw_device_front(drawing, device, start, end, text): + def _draw_device_front(self, drawing, device, start, end, text): name = str(device) if device.devicebay_count: name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count) @@ -69,14 +72,13 @@ class RackElevationSVG: link.add(drawing.text(str(name), insert=text, fill=hex_color)) # Embed front device type image if one exists - if device.device_type.front_image: + if self.include_images and device.device_type.front_image: url = device.device_type.front_image.url image = drawing.image(href=url, insert=start, size=end, class_='device-image') image.stretch() link.add(image) - @staticmethod - def _draw_device_rear(drawing, device, start, end, text): + def _draw_device_rear(self, drawing, device, start, end, text): rect = drawing.rect(start, end, class_="slot blocked") rect.set_desc('{} โ€” {} ({}U) {} {}'.format( device.device_role, device.device_type.display_name, @@ -86,7 +88,7 @@ class RackElevationSVG: drawing.add(drawing.text(str(device), insert=text)) # Embed rear device type image if one exists - if device.device_type.front_image: + if self.include_images and device.device_type.front_image: url = device.device_type.rear_image.url image = drawing.image(href=url, insert=start, size=end, class_='device-image') image.stretch() @@ -128,11 +130,12 @@ class RackElevationSVG: return elevation - def render(self, reserved_units, face, unit_width, unit_height, legend_width): + def render(self, face, unit_width, unit_height, legend_width): """ Return an SVG document representing a rack elevation. """ drawing = self._setup_drawing(unit_width + legend_width, unit_height * self.rack.u_height) + reserved_units = self.rack.get_reserved_units() unit_cursor = 0 for ru in range(0, self.rack.u_height): diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 25f2217b6..5848a6201 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -666,7 +666,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel): face=DeviceFaceChoices.FACE_FRONT, unit_width=RACK_ELEVATION_UNIT_WIDTH_DEFAULT, unit_height=RACK_ELEVATION_UNIT_HEIGHT_DEFAULT, - legend_width=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT + legend_width=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT, + include_images=True ): """ Return an SVG of the rack elevation @@ -676,11 +677,11 @@ class Rack(ChangeLoggedModel, CustomFieldModel): :param unit_height: Height of each rack unit for the rendered drawing. Note this is not the total height of the elevation :param legend_width: Width of the unit legend, in pixels + :param include_images: Embed front/rear device images where available """ - elevation = RackElevationSVG(self) - reserved_units = self.get_reserved_units() + elevation = RackElevationSVG(self, include_images=include_images) - return elevation.render(reserved_units, face, unit_width, unit_height, legend_width) + return elevation.render(face, unit_width, unit_height, legend_width) def get_0u_devices(self): return self.devices.filter(position=0) From c78d30d47e9158022332dab33ef100ed117537b8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 13:20:58 -0500 Subject: [PATCH 26/45] Enable toggling of device images on elevations list --- netbox/project-static/js/rack_elevations.js | 16 ++++++++++ netbox/templates/dcim/rack.html | 29 +++---------------- .../templates/dcim/rack_elevation_list.html | 10 +++---- 3 files changed, 25 insertions(+), 30 deletions(-) create mode 100644 netbox/project-static/js/rack_elevations.js diff --git a/netbox/project-static/js/rack_elevations.js b/netbox/project-static/js/rack_elevations.js new file mode 100644 index 000000000..138065e3c --- /dev/null +++ b/netbox/project-static/js/rack_elevations.js @@ -0,0 +1,16 @@ +// Toggle the display of device images within an SVG rack elevation +$('button.toggle-images').click(function() { + var selected = $(this).attr('selected'); + var rack_front = $("#rack_front"); + var rack_rear = $("#rack_rear"); + if (selected) { + $('.device-image', rack_front.contents()).addClass('hidden'); + $('.device-image', rack_rear.contents()).addClass('hidden'); + } else { + $('.device-image', rack_front.contents()).removeClass('hidden'); + $('.device-image', rack_rear.contents()).removeClass('hidden'); + } + $(this).attr('selected', !selected); + $(this).children('span').toggleClass('glyphicon-check glyphicon-unchecked'); + return false; +}); diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index a43b00f54..b43a4bfdf 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -2,6 +2,7 @@ {% load buttons %} {% load custom_links %} {% load helpers %} +{% load static %} {% block header %}
@@ -45,12 +46,10 @@

{% block title %}Rack {{ rack }}{% endblock %}

{% include 'inc/created_updated.html' with obj=rack %}
- {% custom_links rack %} -
-
- + {% custom_links rack %}
{% include 'inc/custom_fields_panel.html' with obj=powerfeed %} {% include 'extras/inc/tags_panel.html' with tags=powerfeed.tags.all url='dcim:powerfeed_list' %} -
-
- Comments -
-
- {% if powerfeed.comments %} - {{ powerfeed.comments|gfm }} - {% else %} - None - {% endif %} -
-
@@ -164,6 +152,18 @@
+
+
+ Comments +
+
+ {% if powerfeed.comments %} + {{ powerfeed.comments|gfm }} + {% else %} + None + {% endif %} +
+
{% endblock %} From 74e3e2e5e1202e7e6e8b686aee95221147b1bfb8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 14:17:18 -0500 Subject: [PATCH 31/45] Changelog for #4213 --- docs/release-notes/version-2.7.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 600b0ea3e..e1c6375de 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -28,6 +28,7 @@ python3 manage.py renaturalize dcim.Interface * [#4202](https://github.com/netbox-community/netbox/issues/4202) - Prevent reassignment to master device when bulk editing VC member interfaces * [#4204](https://github.com/netbox-community/netbox/issues/4204) - Fix assignment of mask length when bulk editing prefixes * [#4211](https://github.com/netbox-community/netbox/issues/4211) - Include trailing text when naturalizing interface names +* [#4213](https://github.com/netbox-community/netbox/issues/4213) - Restore display of tags and custom fields on power feed view --- From c0052eb416fa9ca2e702d6fbb3cc3ee92548ca18 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 14:24:22 -0500 Subject: [PATCH 32/45] Closes #4209: Enable filtering interfaces list view by enabled --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/forms.py | 6 ++++++ netbox/dcim/tables.py | 5 +++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index e1c6375de..dfb25d75f 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -14,6 +14,7 @@ python3 manage.py renaturalize dcim.Interface * [#3810](https://github.com/netbox-community/netbox/issues/3810) - Preserve slug value when editing existing objects * [#3840](https://github.com/netbox-community/netbox/issues/3840) - Enhance search function when selecting VLANs for interface assignment * [#4170](https://github.com/netbox-community/netbox/issues/4170) - Improve color contrast in rack elevation drawings +* [#4209](https://github.com/netbox-community/netbox/issues/4209) - Enable filtering interfaces list view by enabled ## Bug Fixes diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 37aecbd53..0e5fb382a 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -2821,6 +2821,12 @@ class PowerOutletBulkDisconnectForm(ConfirmationForm): class InterfaceFilterForm(DeviceComponentFilterForm): model = Interface + enabled = forms.NullBooleanField( + required=False, + widget=StaticSelect2( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) tag = TagFilterField(model) diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 1cc438f38..bc91dd70c 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -795,11 +795,12 @@ class InterfaceTable(BaseTable): class InterfaceDetailTable(DeviceComponentDetailTable): parent = tables.LinkColumn(order_by=('device', 'virtual_machine')) name = tables.LinkColumn() + enabled = BooleanColumn() class Meta(InterfaceTable.Meta): order_by = ('parent', 'name') - fields = ('pk', 'parent', 'name', 'type', 'description', 'cable') - sequence = ('pk', 'parent', 'name', 'type', 'description', 'cable') + fields = ('pk', 'parent', 'name', 'enabled', 'type', 'description', 'cable') + sequence = ('pk', 'parent', 'name', 'enabled', 'type', 'description', 'cable') class FrontPortTable(BaseTable): From 682fd40fff828ab4e7716759dedd4ad9a07630cc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 14:27:26 -0500 Subject: [PATCH 33/45] Changelog for #4206 --- docs/release-notes/version-2.7.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index dfb25d75f..aaa93b17b 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -14,6 +14,7 @@ python3 manage.py renaturalize dcim.Interface * [#3810](https://github.com/netbox-community/netbox/issues/3810) - Preserve slug value when editing existing objects * [#3840](https://github.com/netbox-community/netbox/issues/3840) - Enhance search function when selecting VLANs for interface assignment * [#4170](https://github.com/netbox-community/netbox/issues/4170) - Improve color contrast in rack elevation drawings +* [#4206](https://github.com/netbox-community/netbox/issues/4206) - Add RJ-11 console port type * [#4209](https://github.com/netbox-community/netbox/issues/4209) - Enable filtering interfaces list view by enabled ## Bug Fixes From 58ff08be4ed3b47fc0ec0a0edc55318d57fdc180 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 14:37:08 -0500 Subject: [PATCH 34/45] #1529: Add front/rear image fields to DeviceType serializer --- netbox/dcim/api/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 3859ecfeb..34d9a7890 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -227,7 +227,8 @@ class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer): model = DeviceType fields = [ 'id', 'manufacturer', 'model', 'slug', 'display_name', 'part_number', 'u_height', 'is_full_depth', - 'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', + 'subdevice_role', 'front_image', 'rear_image', 'comments', 'tags', 'custom_fields', 'created', + 'last_updated', 'device_count', ] From 2b134ea0f08c43dcc179a078db1da67bc4254dd8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 14:48:23 -0500 Subject: [PATCH 35/45] Release v2.7.7 --- docs/release-notes/version-2.7.md | 2 +- netbox/netbox/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index aaa93b17b..95cdb70dd 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -1,4 +1,4 @@ -# v2.7.7 (FUTURE) +# v2.7.7 (2020-02-20) **Note:** This release fixes a bug affecting the natural ordering of interfaces. If any interfaces appear unordered in NetBox, run the following management command to recalculate their naturalized values after upgrading: diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 249ee9e53..6c33e5b54 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured # Environment setup # -VERSION = '2.7.7-dev' +VERSION = '2.7.7' # Hostname HOSTNAME = platform.node() From 493d68a57a8ecf018b42a749ab579caa0413d03b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Feb 2020 14:59:00 -0500 Subject: [PATCH 36/45] Post-release version bump --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 6c33e5b54..adfc25e09 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured # Environment setup # -VERSION = '2.7.7' +VERSION = '2.7.8-dev' # Hostname HOSTNAME = platform.node() From 0b3111c47f8700ce5560ca9d0b5a4e14ce2d9056 Mon Sep 17 00:00:00 2001 From: LuPo <29709954@nwu.ac.za> Date: Fri, 21 Feb 2020 13:42:52 +0200 Subject: [PATCH 37/45] Fix drawing rear elevation when full-height device has been assigned only front image --- netbox/dcim/elevations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/elevations.py b/netbox/dcim/elevations.py index ed4a3d10a..13af290f0 100644 --- a/netbox/dcim/elevations.py +++ b/netbox/dcim/elevations.py @@ -88,7 +88,7 @@ class RackElevationSVG: drawing.add(drawing.text(str(device), insert=text)) # Embed rear device type image if one exists - if self.include_images and device.device_type.front_image: + if self.include_images and device.device_type.rear_image: url = device.device_type.rear_image.url image = drawing.image(href=url, insert=start, size=end, class_='device-image') image.stretch() From cf312e969047948cc67c2927ba6d63c40c02f85a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 09:42:07 -0500 Subject: [PATCH 38/45] Changelog for #4224 --- docs/release-notes/version-2.7.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 95cdb70dd..f99611d50 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -1,3 +1,11 @@ +# v2.7.8 (FUTURE) + +## Bug Fixes + +* [#4224](https://github.com/netbox-community/netbox/issues/4224) - Fix display of rear device image if front image is not defined + +--- + # v2.7.7 (2020-02-20) **Note:** This release fixes a bug affecting the natural ordering of interfaces. If any interfaces appear unordered in From 5a02dc457cdf824150e087c5aa60104ba0400d0e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 12:43:24 -0500 Subject: [PATCH 39/45] Fixes #4228: Fit device images to rack unit; tweak default aspect ratio --- netbox/dcim/constants.py | 6 +++--- netbox/dcim/elevations.py | 30 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index 13a5052e4..39f30639e 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -9,10 +9,10 @@ from .choices import InterfaceTypeChoices RACK_U_HEIGHT_DEFAULT = 42 +RACK_ELEVATION_BORDER_WIDTH = 2 RACK_ELEVATION_LEGEND_WIDTH_DEFAULT = 30 - -RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 230 -RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 20 +RACK_ELEVATION_UNIT_WIDTH_DEFAULT = 220 +RACK_ELEVATION_UNIT_HEIGHT_DEFAULT = 22 # diff --git a/netbox/dcim/elevations.py b/netbox/dcim/elevations.py index 13af290f0..c8693cb56 100644 --- a/netbox/dcim/elevations.py +++ b/netbox/dcim/elevations.py @@ -6,6 +6,7 @@ from django.utils.http import urlencode from utilities.utils import foreground_color from .choices import DeviceFaceChoices +from .constants import RACK_ELEVATION_BORDER_WIDTH class RackElevationSVG: @@ -75,7 +76,7 @@ class RackElevationSVG: if self.include_images and device.device_type.front_image: url = device.device_type.front_image.url image = drawing.image(href=url, insert=start, size=end, class_='device-image') - image.stretch() + image.fit(scale='slice') link.add(image) def _draw_device_rear(self, drawing, device, start, end, text): @@ -91,7 +92,7 @@ class RackElevationSVG: if self.include_images and device.device_type.rear_image: url = device.device_type.rear_image.url image = drawing.image(href=url, insert=start, size=end, class_='device-image') - image.stretch() + image.fit(scale='slice') drawing.add(image) @staticmethod @@ -134,13 +135,16 @@ class RackElevationSVG: """ Return an SVG document representing a rack elevation. """ - drawing = self._setup_drawing(unit_width + legend_width, unit_height * self.rack.u_height) + drawing = self._setup_drawing( + unit_width + legend_width + RACK_ELEVATION_BORDER_WIDTH * 2, + unit_height * self.rack.u_height + RACK_ELEVATION_BORDER_WIDTH * 2 + ) reserved_units = self.rack.get_reserved_units() unit_cursor = 0 for ru in range(0, self.rack.u_height): start_y = ru * unit_height - position_coordinates = (legend_width / 2, start_y + unit_height / 2 + 2) + position_coordinates = (legend_width / 2, start_y + unit_height / 2 + RACK_ELEVATION_BORDER_WIDTH) unit = ru + 1 if self.rack.desc_units else self.rack.u_height - ru drawing.add( drawing.text(str(unit), position_coordinates, class_="unit") @@ -153,11 +157,12 @@ class RackElevationSVG: height = unit.get('height', 1) # Setup drawing coordinates - start_y = unit_cursor * unit_height + x_offset = legend_width + RACK_ELEVATION_BORDER_WIDTH + y_offset = unit_cursor * unit_height + RACK_ELEVATION_BORDER_WIDTH end_y = unit_height * height - start_cordinates = (legend_width, start_y) - end_cordinates = (legend_width + unit_width, end_y) - text_cordinates = (legend_width + (unit_width / 2), start_y + end_y / 2) + start_cordinates = (x_offset, y_offset) + end_cordinates = (unit_width, end_y) + text_cordinates = (x_offset + (unit_width / 2), y_offset + end_y / 2) # Draw the device if device and device.face == face: @@ -187,6 +192,13 @@ class RackElevationSVG: unit_cursor += height # Wrap the drawing with a border - drawing.add(drawing.rect((legend_width, 1), (unit_width - 1, self.rack.u_height * unit_height - 2), class_='rack')) + border_width = RACK_ELEVATION_BORDER_WIDTH + border_offset = RACK_ELEVATION_BORDER_WIDTH / 2 + frame = drawing.rect( + insert=(legend_width + border_offset, border_offset), + size=(unit_width + border_width, self.rack.u_height * unit_height + border_width), + class_='rack' + ) + drawing.add(frame) return drawing From 8ffba6a279484fe22bdcd4c0013a78b76d0b9a19 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 13:19:45 -0500 Subject: [PATCH 40/45] Clean up rack view CSS --- netbox/project-static/css/base.css | 4 ++-- netbox/templates/dcim/inc/rack_elevation.html | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/netbox/project-static/css/base.css b/netbox/project-static/css/base.css index 281e5d9b7..ec45f43e5 100644 --- a/netbox/project-static/css/base.css +++ b/netbox/project-static/css/base.css @@ -179,9 +179,9 @@ nav ul.pagination { /* Racks */ div.rack_header { - margin-left: 30px; + margin-left: 32px; text-align: center; - width: 230px; + width: 220px; } /* Devices */ diff --git a/netbox/templates/dcim/inc/rack_elevation.html b/netbox/templates/dcim/inc/rack_elevation.html index feced6a22..e3ad8a221 100644 --- a/netbox/templates/dcim/inc/rack_elevation.html +++ b/netbox/templates/dcim/inc/rack_elevation.html @@ -1,4 +1 @@ -{% load helpers %} -
- -
+ From 329740d2a7025e5aabfe52c0c87d087cf9337305 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 13:28:18 -0500 Subject: [PATCH 41/45] Add "Save SVG" link beneath rack elevation display --- netbox/templates/dcim/inc/rack_elevation.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/netbox/templates/dcim/inc/rack_elevation.html b/netbox/templates/dcim/inc/rack_elevation.html index e3ad8a221..e020c44d6 100644 --- a/netbox/templates/dcim/inc/rack_elevation.html +++ b/netbox/templates/dcim/inc/rack_elevation.html @@ -1 +1,6 @@ + From a14c7980f6ae2623d6b2bc645d085acc5b8d4f6f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 13:49:28 -0500 Subject: [PATCH 42/45] Fixes #4232: Enforce consistent background striping in rack elevations --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/elevations.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index f99611d50..6493a7ea3 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -3,6 +3,7 @@ ## Bug Fixes * [#4224](https://github.com/netbox-community/netbox/issues/4224) - Fix display of rear device image if front image is not defined +* [#4232](https://github.com/netbox-community/netbox/issues/4232) - Enforce consistent background striping in rack elevations --- diff --git a/netbox/dcim/elevations.py b/netbox/dcim/elevations.py index 13af290f0..17a5ed87e 100644 --- a/netbox/dcim/elevations.py +++ b/netbox/dcim/elevations.py @@ -22,8 +22,8 @@ class RackElevationSVG: @staticmethod def _add_gradient(drawing, id_, color): gradient = drawing.linearGradient( - start=('0', '0%'), - end=('0', '5%'), + start=(0, 0), + end=(0, 25), spreadMethod='repeat', id_=id_, gradientTransform='rotate(45, 0, 0)', From 45cdac6f36a675bae3663b391e86d9d56f9b5103 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 14:19:02 -0500 Subject: [PATCH 43/45] Changelog for #4228 --- docs/release-notes/version-2.7.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 6493a7ea3..434a0251c 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -3,6 +3,7 @@ ## Bug Fixes * [#4224](https://github.com/netbox-community/netbox/issues/4224) - Fix display of rear device image if front image is not defined +* [#4228](https://github.com/netbox-community/netbox/issues/4228) - Improve fit of device images in rack elevations * [#4232](https://github.com/netbox-community/netbox/issues/4232) - Enforce consistent background striping in rack elevations --- From 11d012de4e4a89aef8a84deaf2c19a4a902dbafc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 14:38:38 -0500 Subject: [PATCH 44/45] Fixes #4235: Fix API representation of content_type for export templates --- docs/release-notes/version-2.7.md | 1 + netbox/extras/api/serializers.py | 3 +++ netbox/extras/tests/test_api.py | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 434a0251c..a7ff3a182 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -5,6 +5,7 @@ * [#4224](https://github.com/netbox-community/netbox/issues/4224) - Fix display of rear device image if front image is not defined * [#4228](https://github.com/netbox-community/netbox/issues/4228) - Improve fit of device images in rack elevations * [#4232](https://github.com/netbox-community/netbox/issues/4232) - Enforce consistent background striping in rack elevations +* [#4235](https://github.com/netbox-community/netbox/issues/4235) - Fix API representation of `content_type` for export templates --- diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 58433df25..20ed3563e 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -62,6 +62,9 @@ class RenderedGraphSerializer(serializers.ModelSerializer): # class ExportTemplateSerializer(ValidatedModelSerializer): + content_type = ContentTypeField( + queryset=ContentType.objects.filter(EXPORTTEMPLATE_MODELS), + ) template_language = ChoiceField( choices=TemplateLanguageChoices, default=TemplateLanguageChoices.LANGUAGE_JINJA2 diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index b15553a49..223e5a530 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -163,17 +163,17 @@ class ExportTemplateTest(APITestCase): super().setUp() - self.content_type = ContentType.objects.get_for_model(Device) + content_type = ContentType.objects.get_for_model(Device) self.exporttemplate1 = ExportTemplate.objects.create( - content_type=self.content_type, name='Test Export Template 1', + content_type=content_type, name='Test Export Template 1', template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' ) self.exporttemplate2 = ExportTemplate.objects.create( - content_type=self.content_type, name='Test Export Template 2', + content_type=content_type, name='Test Export Template 2', template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' ) self.exporttemplate3 = ExportTemplate.objects.create( - content_type=self.content_type, name='Test Export Template 3', + content_type=content_type, name='Test Export Template 3', template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' ) @@ -194,7 +194,7 @@ class ExportTemplateTest(APITestCase): def test_create_exporttemplate(self): data = { - 'content_type': self.content_type.pk, + 'content_type': 'dcim.device', 'name': 'Test Export Template 4', 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', } @@ -205,7 +205,7 @@ class ExportTemplateTest(APITestCase): self.assertHttpStatus(response, status.HTTP_201_CREATED) self.assertEqual(ExportTemplate.objects.count(), 4) exporttemplate4 = ExportTemplate.objects.get(pk=response.data['id']) - self.assertEqual(exporttemplate4.content_type_id, data['content_type']) + self.assertEqual(exporttemplate4.content_type, ContentType.objects.get_for_model(Device)) self.assertEqual(exporttemplate4.name, data['name']) self.assertEqual(exporttemplate4.template_code, data['template_code']) @@ -213,17 +213,17 @@ class ExportTemplateTest(APITestCase): data = [ { - 'content_type': self.content_type.pk, + 'content_type': 'dcim.device', 'name': 'Test Export Template 4', 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', }, { - 'content_type': self.content_type.pk, + 'content_type': 'dcim.device', 'name': 'Test Export Template 5', 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', }, { - 'content_type': self.content_type.pk, + 'content_type': 'dcim.device', 'name': 'Test Export Template 6', 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', }, @@ -241,7 +241,7 @@ class ExportTemplateTest(APITestCase): def test_update_exporttemplate(self): data = { - 'content_type': self.content_type.pk, + 'content_type': 'dcim.device', 'name': 'Test Export Template X', 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', } From 12602a95ea36b79f6bb84867d5e84d0192dee043 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Feb 2020 14:44:42 -0500 Subject: [PATCH 45/45] All fields on RenderedGraphSerializer should be read-only --- netbox/extras/api/serializers.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 20ed3563e..40606ed8e 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -40,10 +40,14 @@ class GraphSerializer(ValidatedModelSerializer): class RenderedGraphSerializer(serializers.ModelSerializer): - embed_url = serializers.SerializerMethodField() - embed_link = serializers.SerializerMethodField() + embed_url = serializers.SerializerMethodField( + read_only=True + ) + embed_link = serializers.SerializerMethodField( + read_only=True + ) type = ContentTypeField( - queryset=ContentType.objects.all() + read_only=True ) class Meta: