From d0bd1ad25b168555725b1f53b4bace0a09cbe413 Mon Sep 17 00:00:00 2001 From: dansheps Date: Mon, 24 Feb 2020 10:18:19 -0600 Subject: [PATCH 01/23] Fixes: #4255 - Add new script variable types based on dynamic model fields --- netbox/extras/scripts.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 57cbc8149..68a1a5ba4 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -18,6 +18,7 @@ from ipam.formfields import IPAddressFormField, IPNetworkFormField from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator from .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING from utilities.exceptions import AbortTransaction +from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField from .forms import ScriptForm from .signals import purge_changelog @@ -197,6 +198,20 @@ class MultiObjectVar(ScriptVariable): self.form_field = TreeNodeMultipleChoiceField +class DynamicObjectVar(ObjectVar): + """ + A dynamic netbox object variable. APISelect will determine the available choices + """ + form_field = DynamicModelChoiceField + + +class DynamicMultiObjectVar(MultiObjectVar): + """ + A multiple choice version of DynamicObjectVar + """ + form_field = DynamicModelMultipleChoiceField + + class FileVar(ScriptVariable): """ An uploaded file. From a5853427d44aa56abb93c74401753a787d59a467 Mon Sep 17 00:00:00 2001 From: dansheps Date: Mon, 24 Feb 2020 10:21:17 -0600 Subject: [PATCH 02/23] Update __all__ for #4255 --- netbox/extras/scripts.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 68a1a5ba4..eb796cf32 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -26,6 +26,8 @@ __all__ = [ 'BaseScript', 'BooleanVar', 'ChoiceVar', + 'DynamicObjectVar', + 'DynamicMultiObjectVar', 'FileVar', 'IntegerVar', 'IPAddressVar', From 8ed0d0400f5dcd5c784fac1fb41b1ec2a0245faf Mon Sep 17 00:00:00 2001 From: dansheps Date: Mon, 24 Feb 2020 10:29:07 -0600 Subject: [PATCH 03/23] Add tests --- netbox/extras/tests/test_scripts.py | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/netbox/extras/tests/test_scripts.py b/netbox/extras/tests/test_scripts.py index 6237d1d95..c926a0b29 100644 --- a/netbox/extras/tests/test_scripts.py +++ b/netbox/extras/tests/test_scripts.py @@ -145,6 +145,30 @@ class ScriptVariablesTest(TestCase): self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['var1'].pk, data['var1']) + def test_dynamicobjectvar(self): + """ + Test dynamic version of the objectvar + """ + + class TestScript(Script): + + var1 = DynamicObjectVar( + queryset=DeviceRole.objects.all() + ) + + # Populate some objects + for i in range(1, 6): + DeviceRole( + name='Device Role {}'.format(i), + slug='device-role-{}'.format(i) + ).save() + + # Validate valid data + data = {'var1': DeviceRole.objects.first().pk} + form = TestScript().as_form(data, None) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['var1'].pk, data['var1']) + def test_multiobjectvar(self): class TestScript(Script): @@ -168,6 +192,32 @@ class ScriptVariablesTest(TestCase): self.assertEqual(form.cleaned_data['var1'][1].pk, data['var1'][1]) self.assertEqual(form.cleaned_data['var1'][2].pk, data['var1'][2]) + def test_dynamicmultiobjectvar(self): + """ + Test dynamic version of the multiobjectvar + """ + + class TestScript(Script): + + var1 = DynamicMultiObjectVar( + queryset=DeviceRole.objects.all() + ) + + # Populate some objects + for i in range(1, 6): + DeviceRole( + name='Device Role {}'.format(i), + slug='device-role-{}'.format(i) + ).save() + + # Validate valid data + data = {'var1': [role.pk for role in DeviceRole.objects.all()[:3]]} + form = TestScript().as_form(data, None) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['var1'][0].pk, data['var1'][0]) + self.assertEqual(form.cleaned_data['var1'][1].pk, data['var1'][1]) + self.assertEqual(form.cleaned_data['var1'][2].pk, data['var1'][2]) + def test_filevar(self): class TestScript(Script): From 27e3b6f377afb8c88e2e79ca805c335d15cff11a Mon Sep 17 00:00:00 2001 From: dansheps Date: Thu, 27 Feb 2020 07:45:11 -0600 Subject: [PATCH 04/23] Remove second variables, make widget mandatory on ObjectVar and MultiObjectVar --- netbox/extras/scripts.py | 28 +++----------- netbox/extras/tests/test_scripts.py | 57 +++-------------------------- 2 files changed, 11 insertions(+), 74 deletions(-) diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index eb796cf32..fdde58a83 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -26,8 +26,6 @@ __all__ = [ 'BaseScript', 'BooleanVar', 'ChoiceVar', - 'DynamicObjectVar', - 'DynamicMultiObjectVar', 'FileVar', 'IntegerVar', 'IPAddressVar', @@ -170,10 +168,10 @@ class ObjectVar(ScriptVariable): """ NetBox object representation. The provided QuerySet will determine the choices available. """ - form_field = forms.ModelChoiceField + form_field = DynamicModelChoiceField - def __init__(self, queryset, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, queryset, widget, *args, **kwargs): + super().__init__(widget=widget, *args, **kwargs) # Queryset for field choices self.field_attrs['queryset'] = queryset @@ -187,10 +185,10 @@ class MultiObjectVar(ScriptVariable): """ Like ObjectVar, but can represent one or more objects. """ - form_field = forms.ModelMultipleChoiceField + form_field = DynamicModelMultipleChoiceField - def __init__(self, queryset, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, queryset, widget, *args, **kwargs): + super().__init__(widget=widget, *args, **kwargs) # Queryset for field choices self.field_attrs['queryset'] = queryset @@ -200,20 +198,6 @@ class MultiObjectVar(ScriptVariable): self.form_field = TreeNodeMultipleChoiceField -class DynamicObjectVar(ObjectVar): - """ - A dynamic netbox object variable. APISelect will determine the available choices - """ - form_field = DynamicModelChoiceField - - -class DynamicMultiObjectVar(MultiObjectVar): - """ - A multiple choice version of DynamicObjectVar - """ - form_field = DynamicModelMultipleChoiceField - - class FileVar(ScriptVariable): """ An uploaded file. diff --git a/netbox/extras/tests/test_scripts.py b/netbox/extras/tests/test_scripts.py index c926a0b29..2d4d5b3fd 100644 --- a/netbox/extras/tests/test_scripts.py +++ b/netbox/extras/tests/test_scripts.py @@ -4,6 +4,7 @@ from netaddr import IPAddress, IPNetwork from dcim.models import DeviceRole from extras.scripts import * +from utilities.forms import APISelect, APISelectMultiple class ScriptVariablesTest(TestCase): @@ -129,31 +130,8 @@ class ScriptVariablesTest(TestCase): class TestScript(Script): var1 = ObjectVar( - queryset=DeviceRole.objects.all() - ) - - # Populate some objects - for i in range(1, 6): - DeviceRole( - name='Device Role {}'.format(i), - slug='device-role-{}'.format(i) - ).save() - - # Validate valid data - data = {'var1': DeviceRole.objects.first().pk} - form = TestScript().as_form(data, None) - self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['var1'].pk, data['var1']) - - def test_dynamicobjectvar(self): - """ - Test dynamic version of the objectvar - """ - - class TestScript(Script): - - var1 = DynamicObjectVar( - queryset=DeviceRole.objects.all() + queryset=DeviceRole.objects.all(), + widget=APISelect(api_url='/api/dcim/device-roles/') ) # Populate some objects @@ -174,33 +152,8 @@ class ScriptVariablesTest(TestCase): class TestScript(Script): var1 = MultiObjectVar( - queryset=DeviceRole.objects.all() - ) - - # Populate some objects - for i in range(1, 6): - DeviceRole( - name='Device Role {}'.format(i), - slug='device-role-{}'.format(i) - ).save() - - # Validate valid data - data = {'var1': [role.pk for role in DeviceRole.objects.all()[:3]]} - form = TestScript().as_form(data, None) - self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['var1'][0].pk, data['var1'][0]) - self.assertEqual(form.cleaned_data['var1'][1].pk, data['var1'][1]) - self.assertEqual(form.cleaned_data['var1'][2].pk, data['var1'][2]) - - def test_dynamicmultiobjectvar(self): - """ - Test dynamic version of the multiobjectvar - """ - - class TestScript(Script): - - var1 = DynamicMultiObjectVar( - queryset=DeviceRole.objects.all() + queryset=DeviceRole.objects.all(), + widget=APISelectMultiple(api_url='/api/dcim/device-roles/') ) # Populate some objects From eab79faaebdc8ee1e5d94c80d1d918d0fa62ae4e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Mar 2020 14:02:24 -0400 Subject: [PATCH 05/23] Changelog for #738 --- 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 b34731403..7395751bb 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -4,6 +4,7 @@ ### Enhancements +* [#738](https://github.com/netbox-community/netbox/issues/738) - Add ability to automatically check for new releases (must be enabled by setting `RELEASE_CHECK_URL`) * [#4309](https://github.com/netbox-community/netbox/issues/4309) - Add descriptive tooltip to custom fields on object views * [#4369](https://github.com/netbox-community/netbox/issues/4369) - Add a dedicated view for rack reservations From 87f0b19dc0147e5329c61f58a1c62d3816c5f5b8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Mar 2020 14:43:19 -0400 Subject: [PATCH 06/23] Closes #4380: Enable webhooks for rack reservations --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/models/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 7395751bb..4a1df8146 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -7,6 +7,7 @@ * [#738](https://github.com/netbox-community/netbox/issues/738) - Add ability to automatically check for new releases (must be enabled by setting `RELEASE_CHECK_URL`) * [#4309](https://github.com/netbox-community/netbox/issues/4309) - Add descriptive tooltip to custom fields on object views * [#4369](https://github.com/netbox-community/netbox/issues/4369) - Add a dedicated view for rack reservations +* [#4380](https://github.com/netbox-community/netbox/issues/4380) - Enable webhooks for rack reservations ### Bug Fixes diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 63c3044c1..d525275fe 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -739,6 +739,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel): return 0 +@extras_features('webhooks') class RackReservation(ChangeLoggedModel): """ One or more reserved units within a Rack. From a9d04547d1a15b70866354358e05d75fbe5288dd Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Mar 2020 14:46:23 -0400 Subject: [PATCH 07/23] Closes #4381: Enable export templates for rack reservations --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/models/__init__.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 4a1df8146..4891071f2 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -8,6 +8,7 @@ * [#4309](https://github.com/netbox-community/netbox/issues/4309) - Add descriptive tooltip to custom fields on object views * [#4369](https://github.com/netbox-community/netbox/issues/4369) - Add a dedicated view for rack reservations * [#4380](https://github.com/netbox-community/netbox/issues/4380) - Enable webhooks for rack reservations +* [#4381](https://github.com/netbox-community/netbox/issues/4381) - Enable export templates for rack reservations ### Bug Fixes diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index d525275fe..23b2618b5 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -739,7 +739,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel): return 0 -@extras_features('webhooks') +@extras_features('export_templates', 'webhooks') class RackReservation(ChangeLoggedModel): """ One or more reserved units within a Rack. From 7f5571200c5deeb0d805fe8bee80af210f70a399 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Mar 2020 14:50:49 -0400 Subject: [PATCH 08/23] Closes #4382: Enable custom links for rack reservations --- docs/release-notes/version-2.7.md | 1 + netbox/dcim/models/__init__.py | 2 +- netbox/templates/dcim/rackreservation.html | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 4891071f2..654f1726d 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -9,6 +9,7 @@ * [#4369](https://github.com/netbox-community/netbox/issues/4369) - Add a dedicated view for rack reservations * [#4380](https://github.com/netbox-community/netbox/issues/4380) - Enable webhooks for rack reservations * [#4381](https://github.com/netbox-community/netbox/issues/4381) - Enable export templates for rack reservations +* [#4382](https://github.com/netbox-community/netbox/issues/4382) - Enable custom links for rack reservations ### Bug Fixes diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 23b2618b5..5b93d3598 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -739,7 +739,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel): return 0 -@extras_features('export_templates', 'webhooks') +@extras_features('custom_links', 'export_templates', 'webhooks') class RackReservation(ChangeLoggedModel): """ One or more reserved units within a Rack. diff --git a/netbox/templates/dcim/rackreservation.html b/netbox/templates/dcim/rackreservation.html index ef9e49d23..be9766557 100644 --- a/netbox/templates/dcim/rackreservation.html +++ b/netbox/templates/dcim/rackreservation.html @@ -36,6 +36,9 @@

{% block title %}{{ rackreservation }}{% endblock %}

{% include 'inc/created_updated.html' with obj=rackreservation %} +
+ {% custom_links rackreservation %} +