nautobot 2.4.3__py3-none-any.whl → 2.4.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/__init__.py +19 -3
- nautobot/apps/filters.py +2 -0
- nautobot/circuits/filters.py +1 -1
- nautobot/circuits/tests/test_models.py +5 -3
- nautobot/cloud/filters.py +3 -6
- nautobot/cloud/tests/test_filters.py +21 -0
- nautobot/core/admin.py +2 -0
- nautobot/core/celery/__init__.py +5 -3
- nautobot/core/jobs/__init__.py +5 -3
- nautobot/core/management/commands/generate_performance_test_endpoints.py +9 -6
- nautobot/core/models/utils.py +6 -1
- nautobot/core/templates/inc/javascript.html +1 -0
- nautobot/core/templatetags/ui_framework.py +20 -4
- nautobot/core/testing/__init__.py +2 -0
- nautobot/core/testing/forms.py +1 -1
- nautobot/core/testing/mixins.py +9 -0
- nautobot/core/tests/test_api.py +1 -1
- nautobot/core/tests/test_graphql.py +3 -3
- nautobot/core/tests/test_jobs.py +30 -28
- nautobot/core/ui/object_detail.py +1 -1
- nautobot/dcim/api/serializers.py +36 -0
- nautobot/dcim/api/views.py +1 -1
- nautobot/dcim/elevations.py +17 -4
- nautobot/dcim/factory.py +9 -1
- nautobot/dcim/filters/__init__.py +27 -1
- nautobot/dcim/forms.py +13 -1
- nautobot/dcim/models/devices.py +11 -5
- nautobot/dcim/signals.py +26 -0
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -62
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +6 -0
- nautobot/dcim/tests/test_api.py +176 -0
- nautobot/dcim/tests/test_filters.py +56 -3
- nautobot/dcim/tests/test_jobs.py +4 -6
- nautobot/dcim/tests/test_models.py +40 -0
- nautobot/dcim/views.py +24 -14
- nautobot/extras/api/mixins.py +1 -1
- nautobot/extras/api/views.py +2 -2
- nautobot/extras/choices.py +8 -3
- nautobot/extras/filters/__init__.py +4 -0
- nautobot/extras/jobs.py +181 -103
- nautobot/extras/management/utils.py +13 -2
- nautobot/extras/models/datasources.py +11 -4
- nautobot/extras/models/jobs.py +20 -17
- nautobot/extras/plugins/__init__.py +26 -1
- nautobot/extras/tables.py +25 -29
- nautobot/extras/templates/extras/inc/jobresult.html +12 -13
- nautobot/extras/templates/extras/objectchange.html +28 -12
- nautobot/extras/test_jobs/atomic_transaction.py +6 -6
- nautobot/extras/test_jobs/fail.py +75 -1
- nautobot/extras/tests/test_api.py +17 -16
- nautobot/extras/tests/test_datasources.py +64 -54
- nautobot/extras/tests/test_filters.py +2 -0
- nautobot/extras/tests/test_jobs.py +69 -62
- nautobot/extras/tests/test_models.py +1 -1
- nautobot/extras/tests/test_plugins.py +32 -1
- nautobot/extras/tests/test_relationships.py +5 -5
- nautobot/extras/tests/test_views.py +12 -2
- nautobot/extras/views.py +10 -1
- nautobot/ipam/api/serializers.py +7 -8
- nautobot/ipam/api/views.py +2 -2
- nautobot/ipam/factory.py +27 -8
- nautobot/ipam/filters.py +67 -29
- nautobot/ipam/formfields.py +51 -0
- nautobot/ipam/forms.py +28 -1
- nautobot/ipam/migrations/0051_added_optional_vrf_relationship_to_vdc.py +41 -0
- nautobot/ipam/models.py +63 -5
- nautobot/ipam/querysets.py +6 -0
- nautobot/ipam/tables.py +21 -7
- nautobot/ipam/templates/ipam/rir.html +1 -43
- nautobot/ipam/tests/test_api.py +107 -66
- nautobot/ipam/tests/test_filters.py +145 -5
- nautobot/ipam/tests/test_models.py +16 -0
- nautobot/ipam/tests/test_views.py +15 -2
- nautobot/ipam/urls.py +1 -21
- nautobot/ipam/views.py +24 -41
- nautobot/project-static/css/base.css +11 -0
- nautobot/project-static/css/dark.css +2 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +62 -0
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +43 -5
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +35 -0
- nautobot/project-static/docs/development/apps/api/configuration-view.html +0 -3
- nautobot/project-static/docs/development/apps/api/models/graphql.html +0 -4
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +94 -1
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +0 -3
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +0 -3
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +0 -3
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +0 -3
- nautobot/project-static/docs/development/apps/api/prometheus.html +0 -3
- nautobot/project-static/docs/development/apps/api/testing.html +0 -6
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +0 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +0 -3
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +0 -3
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +0 -3
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +1 -7
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +0 -7
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +0 -4
- nautobot/project-static/docs/development/apps/api/views/notes.html +0 -3
- nautobot/project-static/docs/development/apps/index.html +2 -35
- nautobot/project-static/docs/development/apps/migration/code-updates.html +1 -1
- nautobot/project-static/docs/development/core/application-registry.html +0 -6
- nautobot/project-static/docs/development/core/best-practices.html +0 -27
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +58 -4
- nautobot/project-static/docs/development/core/getting-started.html +12 -16
- nautobot/project-static/docs/development/core/homepage.html +0 -3
- nautobot/project-static/docs/development/core/style-guide.html +0 -5
- nautobot/project-static/docs/development/core/templates.html +0 -3
- nautobot/project-static/docs/development/core/testing.html +0 -9
- nautobot/project-static/docs/development/jobs/index.html +30 -43
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +0 -18
- nautobot/project-static/docs/release-notes/version-2.4.html +374 -0
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +290 -290
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +0 -10
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +0 -15
- nautobot/project-static/docs/user-guide/administration/installation/index.html +0 -16
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +1 -4
- nautobot/project-static/docs/user-guide/administration/installation/services.html +0 -11
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +3 -3
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +5 -35
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/tables/v2-code-location-changes.yaml +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +1 -1
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +1 -17
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +1 -7
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +0 -6
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +0 -3
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +0 -4
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +0 -8
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +3 -3
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +0 -6
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +3 -15
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +0 -26
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +0 -8
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +0 -8
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +0 -7
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +0 -14
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +1 -10
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +0 -3
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +0 -14
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +0 -19
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +3 -9
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +0 -8
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +0 -4
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +1 -13
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +0 -5
- nautobot/project-static/js/editor.js +292 -0
- nautobot/project-static/monaco-editor-0.52.2/README.md +81 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/base/worker/workerMain.js +31 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/basic-languages/xml/xml.js +10 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/basic-languages/yaml/yaml.js +10 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/editor/editor.main.css +8 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/editor/editor.main.js +798 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/language/json/jsonMode.js +19 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/language/json/jsonWorker.js +42 -0
- nautobot/project-static/monaco-editor-0.52.2/vs/loader.js +11 -0
- nautobot/tenancy/filters/__init__.py +3 -5
- nautobot/tenancy/tests/test_filters.py +10 -0
- nautobot/virtualization/views.py +0 -1
- nautobot/wireless/tables.py +9 -4
- nautobot/wireless/tests/test_api.py +0 -9
- {nautobot-2.4.3.dist-info → nautobot-2.4.5.dist-info}/METADATA +4 -4
- {nautobot-2.4.3.dist-info → nautobot-2.4.5.dist-info}/RECORD +198 -186
- {nautobot-2.4.3.dist-info → nautobot-2.4.5.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.3.dist-info → nautobot-2.4.5.dist-info}/NOTICE +0 -0
- {nautobot-2.4.3.dist-info → nautobot-2.4.5.dist-info}/WHEEL +0 -0
- {nautobot-2.4.3.dist-info → nautobot-2.4.5.dist-info}/entry_points.txt +0 -0
nautobot/ipam/tests/test_api.py
CHANGED
|
@@ -13,7 +13,15 @@ from rest_framework import status
|
|
|
13
13
|
from nautobot.core.testing import APITestCase, APIViewTestCases, disable_warnings
|
|
14
14
|
from nautobot.core.testing.api import APITransactionTestCase
|
|
15
15
|
from nautobot.dcim.choices import InterfaceTypeChoices
|
|
16
|
-
from nautobot.dcim.models import
|
|
16
|
+
from nautobot.dcim.models import (
|
|
17
|
+
Device,
|
|
18
|
+
DeviceType,
|
|
19
|
+
Interface,
|
|
20
|
+
Location,
|
|
21
|
+
LocationType,
|
|
22
|
+
Manufacturer,
|
|
23
|
+
VirtualDeviceContext,
|
|
24
|
+
)
|
|
17
25
|
from nautobot.extras.models import CustomField, Role, Status
|
|
18
26
|
from nautobot.ipam import choices
|
|
19
27
|
from nautobot.ipam.models import (
|
|
@@ -118,6 +126,7 @@ class VRFDeviceAssignmentTest(APIViewTestCases.APIViewTestCase):
|
|
|
118
126
|
def setUpTestData(cls):
|
|
119
127
|
cls.vrfs = VRF.objects.all()
|
|
120
128
|
cls.devices = Device.objects.all()
|
|
129
|
+
cls.vdcs = VirtualDeviceContext.objects.all()
|
|
121
130
|
locations = Location.objects.filter(location_type__name="Campus")
|
|
122
131
|
cluster_type = ClusterType.objects.create(name="Test Cluster Type")
|
|
123
132
|
clusters = (
|
|
@@ -154,25 +163,42 @@ class VRFDeviceAssignmentTest(APIViewTestCases.APIViewTestCase):
|
|
|
154
163
|
virtual_machine=cls.test_vm,
|
|
155
164
|
rd="65000:4",
|
|
156
165
|
)
|
|
166
|
+
VRFDeviceAssignment.objects.create(
|
|
167
|
+
vrf=cls.vrfs[0],
|
|
168
|
+
virtual_device_context=cls.vdcs[0],
|
|
169
|
+
name="VRFDeviceAssignment 1",
|
|
170
|
+
rd="65000:5",
|
|
171
|
+
)
|
|
172
|
+
VRFDeviceAssignment.objects.create(
|
|
173
|
+
vrf=cls.vrfs[0],
|
|
174
|
+
virtual_device_context=cls.vdcs[1],
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
cls.update_data = {
|
|
178
|
+
"name": "VRFDeviceAssignment 2",
|
|
179
|
+
"rd": "65000:7",
|
|
180
|
+
}
|
|
157
181
|
|
|
158
182
|
cls.create_data = [
|
|
159
183
|
{
|
|
160
184
|
"vrf": cls.vrfs[2].pk,
|
|
161
185
|
"device": cls.devices[4].pk,
|
|
162
|
-
"
|
|
163
|
-
"rd": "65000:4",
|
|
186
|
+
"rd": "65000:7",
|
|
164
187
|
},
|
|
165
188
|
{
|
|
166
189
|
"vrf": cls.vrfs[3].pk,
|
|
167
|
-
"device": None,
|
|
168
190
|
"virtual_machine": cls.test_vm.pk,
|
|
169
|
-
"rd": "65000:
|
|
191
|
+
"rd": "65000:8",
|
|
170
192
|
},
|
|
171
193
|
{
|
|
172
194
|
"vrf": cls.vrfs[4].pk,
|
|
173
195
|
"device": cls.devices[6].pk,
|
|
174
|
-
"
|
|
175
|
-
"rd": "65000:
|
|
196
|
+
"name": "VRFDeviceAssignment 3",
|
|
197
|
+
"rd": "65000:9",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"vrf": cls.vrfs[4].pk,
|
|
201
|
+
"virtual_device_context": cls.vdcs[0].pk,
|
|
176
202
|
},
|
|
177
203
|
]
|
|
178
204
|
cls.bulk_update_data = {
|
|
@@ -181,39 +207,66 @@ class VRFDeviceAssignmentTest(APIViewTestCases.APIViewTestCase):
|
|
|
181
207
|
|
|
182
208
|
def test_creating_invalid_vrf_device_assignments(self):
|
|
183
209
|
# Add object-level permission
|
|
184
|
-
duplicate_device_create_data = {
|
|
185
|
-
"vrf": self.vrfs[0].pk,
|
|
186
|
-
"device": self.devices[1].pk,
|
|
187
|
-
"virtual_machine": None,
|
|
188
|
-
"rd": "65000:6",
|
|
189
|
-
}
|
|
190
|
-
duplicate_vm_create_data = {
|
|
191
|
-
"vrf": self.vrfs[1].pk,
|
|
192
|
-
"device": None,
|
|
193
|
-
"virtual_machine": self.test_vm.pk,
|
|
194
|
-
"rd": "65000:6",
|
|
195
|
-
}
|
|
196
|
-
invalid_create_data = {
|
|
197
|
-
"vrf": self.vrfs[2].pk,
|
|
198
|
-
"device": self.devices[6].pk,
|
|
199
|
-
"virtual_machine": self.test_vm.pk,
|
|
200
|
-
"rd": "65000:6",
|
|
201
|
-
}
|
|
202
210
|
self.add_permissions("ipam.add_vrfdeviceassignment")
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
duplicate_create_data = [
|
|
212
|
+
{
|
|
213
|
+
"vrf": self.vrfs[0].pk,
|
|
214
|
+
"device": self.devices[1].pk,
|
|
215
|
+
"rd": "65000:6",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"vrf": self.vrfs[1].pk,
|
|
219
|
+
"virtual_machine": self.test_vm.pk,
|
|
220
|
+
"rd": "65000:6",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"vrf": self.vrfs[0].pk,
|
|
224
|
+
"virtual_device_context": self.vdcs[1].pk,
|
|
225
|
+
"rd": "65000:6",
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
expected_responses = [
|
|
229
|
+
"The fields device, vrf must make a unique set.",
|
|
230
|
+
"The fields virtual_machine, vrf must make a unique set.",
|
|
231
|
+
"The fields virtual_device_context, vrf must make a unique set.",
|
|
232
|
+
]
|
|
233
|
+
for i, data in enumerate(duplicate_create_data):
|
|
234
|
+
response = self.client.post(self._get_list_url(), data, format="json", **self.header)
|
|
235
|
+
self.assertContains(response, expected_responses[i], status_code=status.HTTP_400_BAD_REQUEST)
|
|
236
|
+
|
|
237
|
+
# Test VRFDeviceAssignment model clean() code paths
|
|
238
|
+
vrf = VRF.objects.create(name="New VRF ", namespace=Namespace.objects.first())
|
|
239
|
+
invalid_create_data = [
|
|
240
|
+
{
|
|
241
|
+
"vrf": vrf.pk,
|
|
242
|
+
"device": self.devices[6].pk,
|
|
243
|
+
"virtual_machine": self.test_vm.pk,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"vrf": vrf.pk,
|
|
247
|
+
"device": self.devices[7].pk,
|
|
248
|
+
"virtual_device_context": self.vdcs[2].pk,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"vrf": vrf.pk,
|
|
252
|
+
"virtual_machine": self.test_vm.pk,
|
|
253
|
+
"virtual_device_context": self.vdcs[3].pk,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"vrf": vrf.pk,
|
|
257
|
+
"name": "VRFDeviceAssignment 5",
|
|
258
|
+
"rd": "65000:6",
|
|
259
|
+
},
|
|
260
|
+
]
|
|
261
|
+
expected_responses = [
|
|
262
|
+
"A VRFDeviceAssignment entry cannot be associated with both a device and a virtual machine.",
|
|
263
|
+
"A VRFDeviceAssignment entry cannot be associated with both a device and a virtual device context.",
|
|
264
|
+
"A VRFDeviceAssignment entry cannot be associated with both a virtual machine and a virtual device context.",
|
|
265
|
+
"A VRFDeviceAssignment entry must be associated with a device, a virtual machine, or a virtual device context.",
|
|
266
|
+
]
|
|
267
|
+
for i, data in enumerate(invalid_create_data):
|
|
268
|
+
response = self.client.post(self._get_list_url(), data, format="json", **self.header)
|
|
269
|
+
self.assertContains(response, expected_responses[i], status_code=status.HTTP_400_BAD_REQUEST)
|
|
217
270
|
|
|
218
271
|
|
|
219
272
|
class VRFPrefixAssignmentTest(APIViewTestCases.APIViewTestCase):
|
|
@@ -221,50 +274,38 @@ class VRFPrefixAssignmentTest(APIViewTestCases.APIViewTestCase):
|
|
|
221
274
|
|
|
222
275
|
@classmethod
|
|
223
276
|
def setUpTestData(cls):
|
|
277
|
+
cls.namespace = (
|
|
278
|
+
Namespace.objects.annotate(prefixes_count=Count("prefixes")).filter(prefixes_count__gte=3).first()
|
|
279
|
+
)
|
|
224
280
|
cls.vrfs = (
|
|
225
|
-
VRF.objects.
|
|
281
|
+
VRF.objects.create(name="TEST VRF 1", namespace=cls.namespace),
|
|
282
|
+
VRF.objects.create(name="TEST VRF 2", namespace=cls.namespace),
|
|
226
283
|
)
|
|
227
|
-
cls.prefixes = Prefix.objects.
|
|
284
|
+
cls.prefixes = Prefix.objects.filter(namespace=cls.namespace)
|
|
228
285
|
|
|
229
|
-
VRFPrefixAssignment.objects.create(
|
|
230
|
-
vrf=cls.vrfs[0],
|
|
231
|
-
prefix=cls.prefixes.filter(namespace=cls.vrfs[0].namespace)[0],
|
|
232
|
-
)
|
|
233
|
-
VRFPrefixAssignment.objects.create(
|
|
234
|
-
vrf=cls.vrfs[0],
|
|
235
|
-
prefix=cls.prefixes.filter(namespace=cls.vrfs[0].namespace)[1],
|
|
236
|
-
)
|
|
237
|
-
VRFPrefixAssignment.objects.create(
|
|
238
|
-
vrf=cls.vrfs[1],
|
|
239
|
-
prefix=cls.prefixes.filter(namespace=cls.vrfs[1].namespace)[0],
|
|
240
|
-
)
|
|
241
|
-
VRFPrefixAssignment.objects.create(
|
|
242
|
-
vrf=cls.vrfs[1],
|
|
243
|
-
prefix=cls.prefixes.filter(namespace=cls.vrfs[1].namespace)[1],
|
|
244
|
-
)
|
|
245
286
|
cls.create_data = [
|
|
246
287
|
{
|
|
247
|
-
"vrf": cls.vrfs[
|
|
248
|
-
"prefix": cls.prefixes.
|
|
288
|
+
"vrf": cls.vrfs[0].pk,
|
|
289
|
+
"prefix": cls.prefixes.first().pk,
|
|
249
290
|
},
|
|
250
291
|
{
|
|
251
|
-
"vrf": cls.vrfs[
|
|
252
|
-
"prefix": cls.prefixes.
|
|
292
|
+
"vrf": cls.vrfs[0].pk,
|
|
293
|
+
"prefix": cls.prefixes.last().pk,
|
|
253
294
|
},
|
|
254
295
|
{
|
|
255
|
-
"vrf": cls.vrfs[
|
|
256
|
-
"prefix": cls.prefixes.
|
|
296
|
+
"vrf": cls.vrfs[1].pk,
|
|
297
|
+
"prefix": cls.prefixes.first().pk,
|
|
257
298
|
},
|
|
258
299
|
]
|
|
259
300
|
|
|
260
301
|
def test_creating_invalid_vrf_prefix_assignments(self):
|
|
261
302
|
duplicate_create_data = {
|
|
262
|
-
"vrf":
|
|
263
|
-
"prefix":
|
|
303
|
+
"vrf": VRFPrefixAssignment.objects.first().vrf.pk,
|
|
304
|
+
"prefix": VRFPrefixAssignment.objects.first().prefix.pk,
|
|
264
305
|
}
|
|
265
306
|
wrong_namespace_create_data = {
|
|
266
307
|
"vrf": self.vrfs[0].pk,
|
|
267
|
-
"prefix":
|
|
308
|
+
"prefix": Prefix.objects.exclude(namespace=self.namespace)[0].pk,
|
|
268
309
|
}
|
|
269
310
|
missing_field_create_data = {
|
|
270
311
|
"vrf": self.vrfs[0].pk,
|
|
@@ -10,6 +10,7 @@ from nautobot.dcim.models import (
|
|
|
10
10
|
Location,
|
|
11
11
|
LocationType,
|
|
12
12
|
Manufacturer,
|
|
13
|
+
VirtualDeviceContext,
|
|
13
14
|
)
|
|
14
15
|
from nautobot.extras.models import Role, Status, Tag
|
|
15
16
|
from nautobot.ipam.choices import PrefixTypeChoices, ServiceProtocolChoices
|
|
@@ -24,7 +25,9 @@ from nautobot.ipam.filters import (
|
|
|
24
25
|
VLANFilterSet,
|
|
25
26
|
VLANGroupFilterSet,
|
|
26
27
|
VLANLocationAssignmentFilterSet,
|
|
28
|
+
VRFDeviceAssignmentFilterSet,
|
|
27
29
|
VRFFilterSet,
|
|
30
|
+
VRFPrefixAssignmentFilterSet,
|
|
28
31
|
)
|
|
29
32
|
from nautobot.ipam.models import (
|
|
30
33
|
IPAddress,
|
|
@@ -39,6 +42,8 @@ from nautobot.ipam.models import (
|
|
|
39
42
|
VLANGroup,
|
|
40
43
|
VLANLocationAssignment,
|
|
41
44
|
VRF,
|
|
45
|
+
VRFDeviceAssignment,
|
|
46
|
+
VRFPrefixAssignment,
|
|
42
47
|
)
|
|
43
48
|
from nautobot.tenancy.models import Tenant
|
|
44
49
|
from nautobot.virtualization.models import (
|
|
@@ -66,12 +71,19 @@ class VRFTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilterT
|
|
|
66
71
|
# skip testing "rd" attribute for generic q filter test as it's not trivially modifiable
|
|
67
72
|
exclude_q_filter_predicates = ["rd"]
|
|
68
73
|
generic_filter_tests = (
|
|
74
|
+
# ("device", "devices__id"),
|
|
75
|
+
# ("device", "devices__name"),
|
|
69
76
|
("export_targets", "export_targets__id"),
|
|
70
77
|
("export_targets", "export_targets__name"),
|
|
71
78
|
("import_targets", "import_targets__id"),
|
|
72
79
|
("import_targets", "import_targets__name"),
|
|
80
|
+
("prefix", "prefixes__id"),
|
|
73
81
|
("name",),
|
|
82
|
+
("namespace", "namespace__id"),
|
|
83
|
+
("namespace", "namespace__name"),
|
|
74
84
|
("rd",),
|
|
85
|
+
# ("virtual_machines", "virtual_machines__id"),
|
|
86
|
+
# ("virtual_machines", "virtual_machines__name"),
|
|
75
87
|
)
|
|
76
88
|
|
|
77
89
|
@classmethod
|
|
@@ -79,6 +91,36 @@ class VRFTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilterT
|
|
|
79
91
|
instance = cls.queryset.first()
|
|
80
92
|
instance.tags.set(Tag.objects.all()[:2])
|
|
81
93
|
|
|
94
|
+
def test_prefix_filter_by_string(self):
|
|
95
|
+
"""Test filtering by prefix strings as an alternative to pk."""
|
|
96
|
+
prefix = self.queryset.filter(prefixes__isnull=False).first().prefixes.first()
|
|
97
|
+
params = {"prefix": [prefix.prefix]}
|
|
98
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
99
|
+
self.filterset(params, self.queryset).qs,
|
|
100
|
+
self.queryset.filter(prefixes__network=prefix.network, prefixes__prefix_length=prefix.prefix_length),
|
|
101
|
+
ordered=False,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class VRFPrefixAssignmentTestCase(FilterTestCases.FilterTestCase):
|
|
106
|
+
queryset = VRFPrefixAssignment.objects.all()
|
|
107
|
+
filterset = VRFPrefixAssignmentFilterSet
|
|
108
|
+
generic_filter_tests = (
|
|
109
|
+
("prefix", "prefix__id"),
|
|
110
|
+
("vrf", "vrf__id"),
|
|
111
|
+
("vrf", "vrf__name"),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def test_prefix_filter_by_string(self):
|
|
115
|
+
"""Test filtering by prefix strings as an alternative to pk."""
|
|
116
|
+
prefix = self.queryset.first().prefix
|
|
117
|
+
params = {"prefix": [prefix.prefix]}
|
|
118
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
119
|
+
self.filterset(params, self.queryset).qs,
|
|
120
|
+
self.queryset.filter(prefix__network=prefix.network, prefix__prefix_length=prefix.prefix_length),
|
|
121
|
+
ordered=False,
|
|
122
|
+
)
|
|
123
|
+
|
|
82
124
|
|
|
83
125
|
class RouteTargetTestCase(FilterTestCases.FilterTestCase, FilterTestCases.TenancyFilterTestCaseMixin):
|
|
84
126
|
queryset = RouteTarget.objects.all()
|
|
@@ -190,15 +232,32 @@ class PrefixLocationAssignmentTestCase(FilterTestCases.FilterTestCase):
|
|
|
190
232
|
# )
|
|
191
233
|
|
|
192
234
|
def test_prefix(self):
|
|
193
|
-
ipv4_prefix =
|
|
194
|
-
ipv6_prefix =
|
|
235
|
+
ipv4_prefix = self.queryset.filter(prefix__ip_version=4).first().prefix
|
|
236
|
+
ipv6_prefix = self.queryset.filter(prefix__ip_version=6).first().prefix
|
|
237
|
+
|
|
238
|
+
params = {"prefix": [ipv4_prefix.prefix, ipv6_prefix.pk]}
|
|
239
|
+
|
|
240
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
241
|
+
self.filterset(params, self.queryset).qs,
|
|
242
|
+
self.queryset.filter(prefix=ipv6_prefix)
|
|
243
|
+
| self.queryset.filter(
|
|
244
|
+
prefix__network=ipv4_prefix.network,
|
|
245
|
+
prefix__prefix_length=ipv4_prefix.prefix_length,
|
|
246
|
+
prefix__broadcast=ipv4_prefix.broadcast,
|
|
247
|
+
),
|
|
248
|
+
ordered=False,
|
|
249
|
+
)
|
|
195
250
|
|
|
196
|
-
params = {"prefix": [ipv4_prefix, ipv6_prefix]}
|
|
197
|
-
prefix_queryset = Prefix.objects.net_equals(ipv4_prefix, ipv6_prefix)
|
|
251
|
+
params = {"prefix": [ipv4_prefix.pk, ipv6_prefix.prefix]}
|
|
198
252
|
|
|
199
253
|
self.assertQuerysetEqualAndNotEmpty(
|
|
200
254
|
self.filterset(params, self.queryset).qs,
|
|
201
|
-
self.queryset.filter(
|
|
255
|
+
self.queryset.filter(prefix=ipv4_prefix)
|
|
256
|
+
| self.queryset.filter(
|
|
257
|
+
prefix__network=ipv6_prefix.network,
|
|
258
|
+
prefix__prefix_length=ipv6_prefix.prefix_length,
|
|
259
|
+
prefix__broadcast=ipv6_prefix.broadcast,
|
|
260
|
+
),
|
|
202
261
|
ordered=False,
|
|
203
262
|
)
|
|
204
263
|
|
|
@@ -289,11 +348,21 @@ class PrefixFilterCustomDataTestCase(TestCase):
|
|
|
289
348
|
self.assertQuerysetEqualAndNotEmpty(
|
|
290
349
|
self.filterset(params, self.queryset).qs, self.queryset.filter(parent=parent4)
|
|
291
350
|
)
|
|
351
|
+
params = {"parent": ["10.0.0.0/16"]}
|
|
352
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
353
|
+
self.filterset(params, self.queryset).qs,
|
|
354
|
+
self.queryset.filter(parent__network="10.0.0.0", parent__prefix_length=16),
|
|
355
|
+
)
|
|
292
356
|
parent6 = Prefix.objects.get(prefix="2001:db8::/32", namespace=self.namespace)
|
|
293
357
|
params = {"parent": [str(parent6.pk)]}
|
|
294
358
|
self.assertQuerysetEqualAndNotEmpty(
|
|
295
359
|
self.filterset(params, self.queryset).qs, self.queryset.filter(parent=parent6)
|
|
296
360
|
)
|
|
361
|
+
params = {"parent": ["2001:db8::/32"]}
|
|
362
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
363
|
+
self.filterset(params, self.queryset).qs,
|
|
364
|
+
self.queryset.filter(parent__network="2001:db8::", parent__prefix_length=32),
|
|
365
|
+
)
|
|
297
366
|
|
|
298
367
|
def test_ip_version(self):
|
|
299
368
|
params = {"ip_version": "6"}
|
|
@@ -980,6 +1049,77 @@ class IPAddressToInterfaceTestCase(FilterTestCases.FilterTestCase):
|
|
|
980
1049
|
)
|
|
981
1050
|
|
|
982
1051
|
|
|
1052
|
+
class VRFDeviceAssignmentTestCase(FilterTestCases.FilterTestCase):
|
|
1053
|
+
queryset = VRFDeviceAssignment.objects.all()
|
|
1054
|
+
filterset = VRFDeviceAssignmentFilterSet
|
|
1055
|
+
generic_filter_tests = (
|
|
1056
|
+
["vrf", "vrf__id"],
|
|
1057
|
+
["vrf", "vrf__name"],
|
|
1058
|
+
["device", "device__id"],
|
|
1059
|
+
["device", "device__name"],
|
|
1060
|
+
["virtual_machine", "virtual_machine__id"],
|
|
1061
|
+
["virtual_machine", "virtual_machine__name"],
|
|
1062
|
+
["virtual_device_context", "virtual_device_context__id"],
|
|
1063
|
+
["virtual_device_context", "virtual_device_context__name"],
|
|
1064
|
+
["name"],
|
|
1065
|
+
["rd"],
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
@classmethod
|
|
1069
|
+
def setUpTestData(cls):
|
|
1070
|
+
# Creating VRFDeviceAssignment instances manually until VRFFactory is enhanced to generate VRFDeviceAssignments
|
|
1071
|
+
cls.vrfs = VRF.objects.all()
|
|
1072
|
+
cls.devices = Device.objects.all()
|
|
1073
|
+
cls.vdcs = VirtualDeviceContext.objects.all()
|
|
1074
|
+
locations = Location.objects.filter(location_type__name="Campus")
|
|
1075
|
+
cluster_type = ClusterType.objects.create(name="Test Cluster Type")
|
|
1076
|
+
cluster = Cluster.objects.create(name="Cluster 1", cluster_type=cluster_type, location=locations[0])
|
|
1077
|
+
vm_status = Status.objects.get_for_model(VirtualMachine).first()
|
|
1078
|
+
vm_role = Role.objects.get_for_model(VirtualMachine).first()
|
|
1079
|
+
cls.test_vm_1 = VirtualMachine.objects.create(
|
|
1080
|
+
cluster=cluster,
|
|
1081
|
+
name="VM 1",
|
|
1082
|
+
role=vm_role,
|
|
1083
|
+
status=vm_status,
|
|
1084
|
+
)
|
|
1085
|
+
cls.test_vm_2 = VirtualMachine.objects.create(
|
|
1086
|
+
cluster=cluster,
|
|
1087
|
+
name="VM 2",
|
|
1088
|
+
role=vm_role,
|
|
1089
|
+
status=vm_status,
|
|
1090
|
+
)
|
|
1091
|
+
VRFDeviceAssignment.objects.create(
|
|
1092
|
+
vrf=cls.vrfs[0],
|
|
1093
|
+
device=cls.devices[0],
|
|
1094
|
+
rd="65000:1",
|
|
1095
|
+
)
|
|
1096
|
+
VRFDeviceAssignment.objects.create(
|
|
1097
|
+
vrf=cls.vrfs[0],
|
|
1098
|
+
device=cls.devices[1],
|
|
1099
|
+
rd="65000:2",
|
|
1100
|
+
)
|
|
1101
|
+
VRFDeviceAssignment.objects.create(
|
|
1102
|
+
vrf=cls.vrfs[0],
|
|
1103
|
+
virtual_machine=cls.test_vm_1,
|
|
1104
|
+
rd="65000:3",
|
|
1105
|
+
)
|
|
1106
|
+
VRFDeviceAssignment.objects.create(
|
|
1107
|
+
vrf=cls.vrfs[1],
|
|
1108
|
+
virtual_machine=cls.test_vm_2,
|
|
1109
|
+
rd="65000:4",
|
|
1110
|
+
)
|
|
1111
|
+
VRFDeviceAssignment.objects.create(
|
|
1112
|
+
vrf=cls.vrfs[0],
|
|
1113
|
+
virtual_device_context=cls.vdcs[0],
|
|
1114
|
+
name="VRFDeviceAssignment 1",
|
|
1115
|
+
rd="65000:5",
|
|
1116
|
+
)
|
|
1117
|
+
VRFDeviceAssignment.objects.create(
|
|
1118
|
+
vrf=cls.vrfs[0],
|
|
1119
|
+
virtual_device_context=cls.vdcs[1],
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
|
|
983
1123
|
class VLANGroupTestCase(FilterTestCases.FilterTestCase):
|
|
984
1124
|
queryset = VLANGroup.objects.all()
|
|
985
1125
|
filterset = VLANGroupFilterSet
|
|
@@ -1301,6 +1301,22 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
|
|
|
1301
1301
|
str(err.exception),
|
|
1302
1302
|
)
|
|
1303
1303
|
|
|
1304
|
+
def test_get_or_create_address_kwarg(self):
|
|
1305
|
+
status = Status.objects.get(name="Active")
|
|
1306
|
+
namespace = Namespace.objects.create(name="Test IPAddress get_or_create with address kwarg")
|
|
1307
|
+
Prefix.objects.create(prefix="10.0.0.0/24", namespace=namespace, status=status)
|
|
1308
|
+
ip_address, created = IPAddress.objects.get_or_create(
|
|
1309
|
+
address="10.0.0.40/32", namespace=namespace, defaults={"status": status}
|
|
1310
|
+
)
|
|
1311
|
+
self.assertEqual(ip_address.host, "10.0.0.40")
|
|
1312
|
+
self.assertEqual(ip_address.mask_length, 32)
|
|
1313
|
+
self.assertTrue(created)
|
|
1314
|
+
_, created = IPAddress.objects.get_or_create(
|
|
1315
|
+
address="10.0.0.40/32", namespace=namespace, defaults={"status": status}
|
|
1316
|
+
)
|
|
1317
|
+
self.assertFalse(created)
|
|
1318
|
+
self.assertTrue(IPAddress.objects.filter(address="10.0.0.40/32", parent__namespace=namespace).exists())
|
|
1319
|
+
|
|
1304
1320
|
def test_create_field_population(self):
|
|
1305
1321
|
"""Test that the various ways of creating an IPAddress result in correctly populated fields."""
|
|
1306
1322
|
if self.namespace != get_default_namespace():
|
|
@@ -2,6 +2,7 @@ import datetime
|
|
|
2
2
|
import random
|
|
3
3
|
|
|
4
4
|
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.db.models import Count
|
|
5
6
|
from django.test import override_settings
|
|
6
7
|
from django.urls import reverse
|
|
7
8
|
from django.utils.html import strip_tags
|
|
@@ -13,7 +14,15 @@ from nautobot.core.templatetags.helpers import hyperlinked_object, queryset_to_p
|
|
|
13
14
|
from nautobot.core.testing import ModelViewTestCase, post_data, ViewTestCases
|
|
14
15
|
from nautobot.core.testing.utils import extract_page_body
|
|
15
16
|
from nautobot.core.utils.lookup import get_route_for_model
|
|
16
|
-
from nautobot.dcim.models import
|
|
17
|
+
from nautobot.dcim.models import (
|
|
18
|
+
Device,
|
|
19
|
+
DeviceType,
|
|
20
|
+
Interface,
|
|
21
|
+
Location,
|
|
22
|
+
LocationType,
|
|
23
|
+
Manufacturer,
|
|
24
|
+
VirtualDeviceContext,
|
|
25
|
+
)
|
|
17
26
|
from nautobot.extras.choices import CustomFieldTypeChoices, RelationshipTypeChoices
|
|
18
27
|
from nautobot.extras.models import (
|
|
19
28
|
CustomField,
|
|
@@ -72,8 +81,9 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
72
81
|
@classmethod
|
|
73
82
|
def setUpTestData(cls):
|
|
74
83
|
tenants = Tenant.objects.all()[:2]
|
|
75
|
-
namespace =
|
|
84
|
+
namespace = Namespace.objects.annotate(prefix_count=Count("prefixes")).filter(prefix_count__gt=2).first()
|
|
76
85
|
prefixes = Prefix.objects.filter(namespace=namespace)
|
|
86
|
+
vdcs = VirtualDeviceContext.objects.all()
|
|
77
87
|
vrf_statuses = Status.objects.get_for_model(VRF)
|
|
78
88
|
|
|
79
89
|
cls.form_data = {
|
|
@@ -85,6 +95,7 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
85
95
|
"prefixes": [prefixes[1].id],
|
|
86
96
|
"tags": [t.pk for t in Tag.objects.get_for_model(VRF)],
|
|
87
97
|
"status": vrf_statuses.first().pk,
|
|
98
|
+
"virtual_device_contexts": [vdcs[0].id, vdcs[1].id],
|
|
88
99
|
}
|
|
89
100
|
|
|
90
101
|
cls.bulk_edit_data = {
|
|
@@ -94,6 +105,8 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|
|
94
105
|
"namespace": prefixes[0].namespace.id,
|
|
95
106
|
"add_prefixes": [prefixes[0].id],
|
|
96
107
|
"remove_prefixes": [prefixes[1].id],
|
|
108
|
+
"add_virtual_device_contexts": [vdcs[2].id, vdcs[3].id],
|
|
109
|
+
"remove_virtual_device_contexts": [vdcs[0].id],
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
|
nautobot/ipam/urls.py
CHANGED
|
@@ -7,7 +7,6 @@ from . import views
|
|
|
7
7
|
from .models import (
|
|
8
8
|
IPAddress,
|
|
9
9
|
Prefix,
|
|
10
|
-
RIR,
|
|
11
10
|
Service,
|
|
12
11
|
VLAN,
|
|
13
12
|
VLANGroup,
|
|
@@ -20,28 +19,9 @@ router.register("ip-address-to-interface", views.IPAddressToInterfaceUIViewSet)
|
|
|
20
19
|
router.register("namespaces", views.NamespaceUIViewSet)
|
|
21
20
|
router.register("route-targets", views.RouteTargetUIViewSet)
|
|
22
21
|
router.register("vrfs", views.VRFUIViewSet)
|
|
22
|
+
router.register("rirs", views.RIRUIViewSet)
|
|
23
23
|
|
|
24
24
|
urlpatterns = [
|
|
25
|
-
# RIRs
|
|
26
|
-
path("rirs/", views.RIRListView.as_view(), name="rir_list"),
|
|
27
|
-
path("rirs/add/", views.RIREditView.as_view(), name="rir_add"),
|
|
28
|
-
path("rirs/import/", views.RIRBulkImportView.as_view(), name="rir_import"), # 3.0 TODO: remove, unused
|
|
29
|
-
path("rirs/delete/", views.RIRBulkDeleteView.as_view(), name="rir_bulk_delete"),
|
|
30
|
-
path("rirs/<uuid:pk>/", views.RIRView.as_view(), name="rir"),
|
|
31
|
-
path("rirs/<uuid:pk>/edit/", views.RIREditView.as_view(), name="rir_edit"),
|
|
32
|
-
path("rirs/<uuid:pk>/delete/", views.RIRDeleteView.as_view(), name="rir_delete"),
|
|
33
|
-
path(
|
|
34
|
-
"rirs/<uuid:pk>/changelog/",
|
|
35
|
-
ObjectChangeLogView.as_view(),
|
|
36
|
-
name="rir_changelog",
|
|
37
|
-
kwargs={"model": RIR},
|
|
38
|
-
),
|
|
39
|
-
path(
|
|
40
|
-
"rirs/<uuid:pk>/notes/",
|
|
41
|
-
ObjectNotesView.as_view(),
|
|
42
|
-
name="rir_notes",
|
|
43
|
-
kwargs={"model": RIR},
|
|
44
|
-
),
|
|
45
25
|
# Namespaces
|
|
46
26
|
path(
|
|
47
27
|
"namespaces/<uuid:pk>/ip-addresses/",
|
nautobot/ipam/views.py
CHANGED
|
@@ -325,49 +325,32 @@ class RouteTargetUIViewSet(NautobotUIViewSet):
|
|
|
325
325
|
#
|
|
326
326
|
|
|
327
327
|
|
|
328
|
-
class
|
|
328
|
+
class RIRUIViewSet(NautobotUIViewSet):
|
|
329
|
+
bulk_update_form_class = forms.RIRBulkEditForm
|
|
330
|
+
filterset_class = filters.RIRFilterSet
|
|
331
|
+
filterset_form_class = forms.RIRFilterForm
|
|
332
|
+
form_class = forms.RIRForm
|
|
329
333
|
queryset = RIR.objects.all()
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
table = tables.RIRTable
|
|
334
|
+
serializer_class = serializers.RIRSerializer
|
|
335
|
+
table_class = tables.RIRTable
|
|
333
336
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
class RIREditView(generic.ObjectEditView):
|
|
354
|
-
queryset = RIR.objects.all()
|
|
355
|
-
model_form = forms.RIRForm
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
class RIRDeleteView(generic.ObjectDeleteView):
|
|
359
|
-
queryset = RIR.objects.all()
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
class RIRBulkImportView(generic.BulkImportView): # 3.0 TODO: remove, unused
|
|
363
|
-
queryset = RIR.objects.all()
|
|
364
|
-
table = tables.RIRTable
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
class RIRBulkDeleteView(generic.BulkDeleteView):
|
|
368
|
-
queryset = RIR.objects.all()
|
|
369
|
-
filterset = filters.RIRFilterSet
|
|
370
|
-
table = tables.RIRTable
|
|
337
|
+
object_detail_content = object_detail.ObjectDetailContent(
|
|
338
|
+
panels=(
|
|
339
|
+
object_detail.ObjectFieldsPanel(
|
|
340
|
+
section=SectionChoices.LEFT_HALF,
|
|
341
|
+
weight=100,
|
|
342
|
+
fields="__all__",
|
|
343
|
+
),
|
|
344
|
+
object_detail.ObjectsTablePanel(
|
|
345
|
+
section=SectionChoices.FULL_WIDTH,
|
|
346
|
+
weight=100,
|
|
347
|
+
table_title="Assigned Prefixes",
|
|
348
|
+
table_class=tables.PrefixTable,
|
|
349
|
+
table_filter="rir",
|
|
350
|
+
hide_hierarchy_ui=True,
|
|
351
|
+
),
|
|
352
|
+
),
|
|
353
|
+
)
|
|
371
354
|
|
|
372
355
|
|
|
373
356
|
#
|
|
@@ -1022,3 +1022,14 @@ a.tile.clickable:visited {
|
|
|
1022
1022
|
text-decoration: none;
|
|
1023
1023
|
background-color: #646464;
|
|
1024
1024
|
}
|
|
1025
|
+
|
|
1026
|
+
/* Monaco Editor container */
|
|
1027
|
+
.editor-container {
|
|
1028
|
+
display: flex; /* Use flexbox for layout */
|
|
1029
|
+
flex-direction: column;
|
|
1030
|
+
flex: 1; /* Allow container to grow */
|
|
1031
|
+
min-height: 100px;
|
|
1032
|
+
position: relative; /* Required for Monaco's absolute positioning */
|
|
1033
|
+
overflow: hidden; /* Let Monaco handle scrolling */
|
|
1034
|
+
}
|
|
1035
|
+
|
|
@@ -21,7 +21,8 @@ html[data-theme="dark"] .color-block, /* Colored choices, li
|
|
|
21
21
|
html[data-theme="dark"] .select2-selection__choice, /* Colored choices, like statuses */
|
|
22
22
|
html[data-theme="dark"] #select2-id_color-results, /* Colored choices, like statuses */
|
|
23
23
|
html[data-theme="dark"] #select2-id_color-container, /* Colored choices, like statuses */
|
|
24
|
-
html[data-theme="dark"] .hljs
|
|
24
|
+
html[data-theme="dark"] .hljs, /* highlight.js maintains its own dark theme */
|
|
25
|
+
html[data-theme="dark"] .editor-container { /* Monaco editor maintains its own dark theme */
|
|
25
26
|
filter: invert(1) hue-rotate(180deg);
|
|
26
27
|
}
|
|
27
28
|
|