nautobot 2.3.0__py3-none-any.whl → 2.3.1__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.

Files changed (40) hide show
  1. nautobot/cloud/forms.py +1 -1
  2. nautobot/cloud/tests/test_views.py +17 -0
  3. nautobot/cloud/views.py +1 -1
  4. nautobot/core/celery/__init__.py +5 -2
  5. nautobot/core/templates/generic/object_retrieve.html +1 -1
  6. nautobot/core/templates/inc/computed_fields/panel_data.html +36 -24
  7. nautobot/core/templates/inc/object_details_advanced_panel.html +1 -1
  8. nautobot/core/views/__init__.py +1 -1
  9. nautobot/dcim/forms.py +19 -0
  10. nautobot/dcim/models/devices.py +12 -15
  11. nautobot/dcim/templates/dcim/device.html +1 -1
  12. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +1 -1
  13. nautobot/dcim/tests/test_forms.py +54 -0
  14. nautobot/dcim/tests/test_models.py +9 -1
  15. nautobot/dcim/tests/test_views.py +6 -2
  16. nautobot/extras/api/serializers.py +0 -1
  17. nautobot/extras/filters/__init__.py +4 -1
  18. nautobot/extras/forms/forms.py +1 -0
  19. nautobot/extras/migrations/0114_computedfield_grouping.py +17 -0
  20. nautobot/extras/models/customfields.py +54 -0
  21. nautobot/extras/templates/extras/computedfield.html +4 -0
  22. nautobot/extras/views.py +1 -1
  23. nautobot/ipam/querysets.py +26 -0
  24. nautobot/ipam/tests/test_models.py +86 -0
  25. nautobot/ipam/views.py +4 -4
  26. nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +0 -45
  27. nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +0 -90
  28. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +127 -0
  29. nautobot/project-static/docs/objects.inv +0 -0
  30. nautobot/project-static/docs/release-notes/version-2.3.html +148 -24
  31. nautobot/project-static/docs/requirements.txt +1 -1
  32. nautobot/project-static/docs/search/search_index.json +1 -1
  33. nautobot/project-static/docs/sitemap.xml +271 -271
  34. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  35. {nautobot-2.3.0.dist-info → nautobot-2.3.1.dist-info}/METADATA +1 -1
  36. {nautobot-2.3.0.dist-info → nautobot-2.3.1.dist-info}/RECORD +40 -39
  37. {nautobot-2.3.0.dist-info → nautobot-2.3.1.dist-info}/LICENSE.txt +0 -0
  38. {nautobot-2.3.0.dist-info → nautobot-2.3.1.dist-info}/NOTICE +0 -0
  39. {nautobot-2.3.0.dist-info → nautobot-2.3.1.dist-info}/WHEEL +0 -0
  40. {nautobot-2.3.0.dist-info → nautobot-2.3.1.dist-info}/entry_points.txt +0 -0
@@ -962,6 +962,92 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
962
962
  self.status = Status.objects.get(name="Active")
963
963
  self.prefix = Prefix.objects.create(prefix="192.0.2.0/24", status=self.status, namespace=self.namespace)
964
964
 
965
+ def test_get_or_create(self):
966
+ """Assert `get_or_create` method to permit specifying a namespace as an alternative to a parent prefix."""
967
+ default_namespace = get_default_namespace()
968
+ namespace = Namespace.objects.create(name="Test Namespace")
969
+
970
+ ipaddr_status = Status.objects.get_for_model(IPAddress).first()
971
+ prefix_status = Status.objects.get_for_model(Prefix).first()
972
+
973
+ parent = Prefix.objects.create(prefix="10.1.1.0/24", namespace=namespace, status=prefix_status)
974
+ default_namespace_parent = Prefix.objects.create(
975
+ prefix="10.0.0.0/24", namespace=default_namespace, status=prefix_status
976
+ )
977
+
978
+ ipaddress = IPAddress.objects.create(address="10.1.1.1/24", namespace=namespace, status=ipaddr_status)
979
+ default_namespace_ipaddress = IPAddress.objects.create(
980
+ address="10.0.0.1/24", namespace=default_namespace, status=ipaddr_status
981
+ )
982
+ mask_length = 24
983
+
984
+ with self.subTest("Assert retrieve"):
985
+ ip_obj, created = IPAddress.objects.get_or_create(
986
+ host=ipaddress.host,
987
+ mask_length=mask_length,
988
+ namespace=namespace,
989
+ status=ipaddr_status,
990
+ )
991
+ self.assertEqual(ip_obj, ipaddress)
992
+ self.assertFalse(created)
993
+
994
+ ip_obj, created = IPAddress.objects.get_or_create(host=ipaddress.host, status=ipaddr_status)
995
+ self.assertEqual(ip_obj.status, ipaddr_status)
996
+ self.assertFalse(created)
997
+
998
+ with self.subTest(
999
+ "Assert get_or_create utilizes default namespace when retrieving parent if no namespace is provided"
1000
+ ):
1001
+ ip_obj, created = IPAddress.objects.get_or_create(
1002
+ host=default_namespace_ipaddress.host,
1003
+ mask_length=default_namespace_ipaddress.mask_length,
1004
+ status=ipaddr_status,
1005
+ )
1006
+ self.assertEqual(ip_obj, default_namespace_ipaddress)
1007
+ self.assertFalse(created)
1008
+
1009
+ with self.subTest("Assert create"):
1010
+ new_host = "10.0.0.2"
1011
+ ip_obj, created = IPAddress.objects.get_or_create(
1012
+ host=new_host,
1013
+ mask_length=mask_length,
1014
+ status=ipaddr_status,
1015
+ )
1016
+ self.assertEqual(ip_obj.host, new_host)
1017
+ self.assertEqual(ip_obj.mask_length, mask_length)
1018
+ self.assertEqual(ip_obj.parent, default_namespace_parent)
1019
+ self.assertEqual(ip_obj.parent.namespace, default_namespace)
1020
+ self.assertTrue(created)
1021
+
1022
+ with self.subTest("Assert create explicitly defining a non default namespace"):
1023
+ new_host = "10.1.1.2"
1024
+ ip_obj, created = IPAddress.objects.get_or_create(
1025
+ host=new_host, mask_length=mask_length, status=ipaddr_status, namespace=namespace
1026
+ )
1027
+ self.assertEqual(ip_obj.host, new_host)
1028
+ self.assertEqual(ip_obj.mask_length, mask_length)
1029
+ self.assertEqual(ip_obj.parent, parent)
1030
+ self.assertEqual(ip_obj.parent.namespace, namespace)
1031
+ self.assertTrue(created)
1032
+
1033
+ with self.subTest("Assert passing invalid host/mask_length"):
1034
+ with self.assertRaises(ValidationError) as err:
1035
+ IPAddress.objects.get_or_create(
1036
+ host="0.000.0", mask_length=mask_length, status=ipaddr_status, namespace=namespace
1037
+ )
1038
+ self.assertIn(
1039
+ "Enter a valid IPv4 or IPv6 address.",
1040
+ str(err.exception),
1041
+ )
1042
+ with self.assertRaises(ValidationError) as err:
1043
+ IPAddress.objects.get_or_create(
1044
+ host=ipaddress.host, mask_length=5712, status=ipaddr_status, namespace=namespace
1045
+ )
1046
+ self.assertIn(
1047
+ f"{ipaddress.host}/5712 does not appear to be an IPv4 or IPv6 network.",
1048
+ str(err.exception),
1049
+ )
1050
+
965
1051
  def test_create_field_population(self):
966
1052
  """Test that the various ways of creating an IPAddress result in correctly populated fields."""
967
1053
  if self.namespace != get_default_namespace():
nautobot/ipam/views.py CHANGED
@@ -250,7 +250,7 @@ class VRFView(generic.ObjectView):
250
250
 
251
251
  prefixes = instance.prefixes.restrict(request.user, "view")
252
252
  prefix_count = prefixes.count()
253
- prefix_table = tables.PrefixTable(prefixes.select_related("namespace"))
253
+ prefix_table = tables.PrefixTable(prefixes.select_related("namespace"), hide_hierarchy_ui=True)
254
254
 
255
255
  # devices = instance.devices.restrict(request.user, "view")
256
256
  # device_count = devices.count()
@@ -390,7 +390,7 @@ class RIRView(generic.ObjectView):
390
390
  # Prefixes
391
391
  assigned_prefixes = Prefix.objects.restrict(request.user, "view").filter(rir=instance).select_related("tenant")
392
392
 
393
- assigned_prefix_table = tables.PrefixTable(assigned_prefixes)
393
+ assigned_prefix_table = tables.PrefixTable(assigned_prefixes, hide_hierarchy_ui=True)
394
394
 
395
395
  paginate = {
396
396
  "paginator_class": EnhancedPaginator,
@@ -485,7 +485,7 @@ class PrefixPrefixesView(generic.ObjectView):
485
485
  if child_prefixes and request.GET.get("show_available", "true") == "true":
486
486
  child_prefixes = add_available_prefixes(instance.prefix, child_prefixes)
487
487
 
488
- prefix_table = tables.PrefixDetailTable(child_prefixes)
488
+ prefix_table = tables.PrefixDetailTable(child_prefixes, hide_hierarchy_ui=True)
489
489
  prefix_table.exclude = ("namespace",)
490
490
  if request.user.has_perm("ipam.change_prefix") or request.user.has_perm("ipam.delete_prefix"):
491
491
  prefix_table.columns.show("pk")
@@ -1313,7 +1313,7 @@ class VLANView(generic.ObjectView):
1313
1313
  "namespace",
1314
1314
  )
1315
1315
  )
1316
- prefix_table = tables.PrefixTable(list(prefixes))
1316
+ prefix_table = tables.PrefixTable(list(prefixes), hide_hierarchy_ui=True)
1317
1317
  prefix_table.exclude = ("vlan",)
1318
1318
 
1319
1319
  return {"prefix_table": prefix_table, **super().get_extra_context(request, instance)}
@@ -7296,21 +7296,6 @@
7296
7296
  </span>
7297
7297
  </a>
7298
7298
 
7299
- <nav class="md-nav" aria-label="TagFilter">
7300
- <ul class="md-nav__list">
7301
-
7302
- <li class="md-nav__item">
7303
- <a href="#nautobot.apps.filters.TagFilter.__init__" class="md-nav__link">
7304
- <span class="md-ellipsis">
7305
- __init__
7306
- </span>
7307
- </a>
7308
-
7309
- </li>
7310
-
7311
- </ul>
7312
- </nav>
7313
-
7314
7299
  </li>
7315
7300
 
7316
7301
  <li class="md-nav__item">
@@ -9368,21 +9353,6 @@
9368
9353
  </span>
9369
9354
  </a>
9370
9355
 
9371
- <nav class="md-nav" aria-label="TagFilter">
9372
- <ul class="md-nav__list">
9373
-
9374
- <li class="md-nav__item">
9375
- <a href="#nautobot.apps.filters.TagFilter.__init__" class="md-nav__link">
9376
- <span class="md-ellipsis">
9377
- __init__
9378
- </span>
9379
- </a>
9380
-
9381
- </li>
9382
-
9383
- </ul>
9384
- </nav>
9385
-
9386
9356
  </li>
9387
9357
 
9388
9358
  <li class="md-nav__item">
@@ -10636,21 +10606,6 @@ to objects matching all tags.</p>
10636
10606
 
10637
10607
 
10638
10608
 
10639
- <div class="doc doc-object doc-function">
10640
-
10641
-
10642
- <h3 id="nautobot.apps.filters.TagFilter.__init__" class="doc doc-heading">
10643
- <code class="highlight language-python"><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></code>
10644
-
10645
- <a href="#nautobot.apps.filters.TagFilter.__init__" class="headerlink" title="Permanent link">&para;</a></h3>
10646
-
10647
-
10648
- <div class="doc doc-contents ">
10649
-
10650
- </div>
10651
-
10652
- </div>
10653
-
10654
10609
 
10655
10610
 
10656
10611
  </div>
@@ -7305,21 +7305,6 @@
7305
7305
  </span>
7306
7306
  </a>
7307
7307
 
7308
- <nav class="md-nav" aria-label="DynamicFilterForm">
7309
- <ul class="md-nav__list">
7310
-
7311
- <li class="md-nav__item">
7312
- <a href="#nautobot.apps.forms.DynamicFilterForm.__init__" class="md-nav__link">
7313
- <span class="md-ellipsis">
7314
- __init__
7315
- </span>
7316
- </a>
7317
-
7318
- </li>
7319
-
7320
- </ul>
7321
- </nav>
7322
-
7323
7308
  </li>
7324
7309
 
7325
7310
  <li class="md-nav__item">
@@ -7803,21 +7788,6 @@
7803
7788
  </span>
7804
7789
  </a>
7805
7790
 
7806
- <nav class="md-nav" aria-label="TagFilterField">
7807
- <ul class="md-nav__list">
7808
-
7809
- <li class="md-nav__item">
7810
- <a href="#nautobot.apps.forms.TagFilterField.__init__" class="md-nav__link">
7811
- <span class="md-ellipsis">
7812
- __init__
7813
- </span>
7814
- </a>
7815
-
7816
- </li>
7817
-
7818
- </ul>
7819
- </nav>
7820
-
7821
7791
  </li>
7822
7792
 
7823
7793
  <li class="md-nav__item">
@@ -9881,21 +9851,6 @@
9881
9851
  </span>
9882
9852
  </a>
9883
9853
 
9884
- <nav class="md-nav" aria-label="DynamicFilterForm">
9885
- <ul class="md-nav__list">
9886
-
9887
- <li class="md-nav__item">
9888
- <a href="#nautobot.apps.forms.DynamicFilterForm.__init__" class="md-nav__link">
9889
- <span class="md-ellipsis">
9890
- __init__
9891
- </span>
9892
- </a>
9893
-
9894
- </li>
9895
-
9896
- </ul>
9897
- </nav>
9898
-
9899
9854
  </li>
9900
9855
 
9901
9856
  <li class="md-nav__item">
@@ -10379,21 +10334,6 @@
10379
10334
  </span>
10380
10335
  </a>
10381
10336
 
10382
- <nav class="md-nav" aria-label="TagFilterField">
10383
- <ul class="md-nav__list">
10384
-
10385
- <li class="md-nav__item">
10386
- <a href="#nautobot.apps.forms.TagFilterField.__init__" class="md-nav__link">
10387
- <span class="md-ellipsis">
10388
- __init__
10389
- </span>
10390
- </a>
10391
-
10392
- </li>
10393
-
10394
- </ul>
10395
- </nav>
10396
-
10397
10337
  </li>
10398
10338
 
10399
10339
  <li class="md-nav__item">
@@ -11651,21 +11591,6 @@ part of a JSON/YAML DeviceType import.</p>
11651
11591
 
11652
11592
 
11653
11593
 
11654
- <div class="doc doc-object doc-function">
11655
-
11656
-
11657
- <h3 id="nautobot.apps.forms.DynamicFilterForm.__init__" class="doc doc-heading">
11658
- <code class="highlight language-python"><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">filterset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></code>
11659
-
11660
- <a href="#nautobot.apps.forms.DynamicFilterForm.__init__" class="headerlink" title="Permanent link">&para;</a></h3>
11661
-
11662
-
11663
- <div class="doc doc-contents ">
11664
-
11665
- </div>
11666
-
11667
- </div>
11668
-
11669
11594
 
11670
11595
 
11671
11596
  </div>
@@ -13402,21 +13327,6 @@ Then verify that any requested RelationshipAssociations do not violate relations
13402
13327
 
13403
13328
 
13404
13329
 
13405
- <div class="doc doc-object doc-function">
13406
-
13407
-
13408
- <h3 id="nautobot.apps.forms.TagFilterField.__init__" class="doc doc-heading">
13409
- <code class="highlight language-python"><span class="fm">__init__</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">query_params</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">queryset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></code>
13410
-
13411
- <a href="#nautobot.apps.forms.TagFilterField.__init__" class="headerlink" title="Permanent link">&para;</a></h3>
13412
-
13413
-
13414
- <div class="doc doc-contents ">
13415
-
13416
- </div>
13417
-
13418
- </div>
13419
-
13420
13330
 
13421
13331
 
13422
13332
  </div>
@@ -7417,6 +7417,33 @@
7417
7417
  </span>
7418
7418
  </a>
7419
7419
 
7420
+ </li>
7421
+
7422
+ <li class="md-nav__item">
7423
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping" class="md-nav__link">
7424
+ <span class="md-ellipsis">
7425
+ get_computed_fields_grouping
7426
+ </span>
7427
+ </a>
7428
+
7429
+ </li>
7430
+
7431
+ <li class="md-nav__item">
7432
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_advanced" class="md-nav__link">
7433
+ <span class="md-ellipsis">
7434
+ get_computed_fields_grouping_advanced
7435
+ </span>
7436
+ </a>
7437
+
7438
+ </li>
7439
+
7440
+ <li class="md-nav__item">
7441
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_basic" class="md-nav__link">
7442
+ <span class="md-ellipsis">
7443
+ get_computed_fields_grouping_basic
7444
+ </span>
7445
+ </a>
7446
+
7420
7447
  </li>
7421
7448
 
7422
7449
  <li class="md-nav__item">
@@ -10278,6 +10305,33 @@
10278
10305
  </span>
10279
10306
  </a>
10280
10307
 
10308
+ </li>
10309
+
10310
+ <li class="md-nav__item">
10311
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping" class="md-nav__link">
10312
+ <span class="md-ellipsis">
10313
+ get_computed_fields_grouping
10314
+ </span>
10315
+ </a>
10316
+
10317
+ </li>
10318
+
10319
+ <li class="md-nav__item">
10320
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_advanced" class="md-nav__link">
10321
+ <span class="md-ellipsis">
10322
+ get_computed_fields_grouping_advanced
10323
+ </span>
10324
+ </a>
10325
+
10326
+ </li>
10327
+
10328
+ <li class="md-nav__item">
10329
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_basic" class="md-nav__link">
10330
+ <span class="md-ellipsis">
10331
+ get_computed_fields_grouping_basic
10332
+ </span>
10333
+ </a>
10334
+
10281
10335
  </li>
10282
10336
 
10283
10337
  <li class="md-nav__item">
@@ -12170,6 +12224,79 @@ Keys are the <code>key</code> value of each field. If label_as_key is True, <cod
12170
12224
  <div class="doc doc-object doc-function">
12171
12225
 
12172
12226
 
12227
+ <h3 id="nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping" class="doc doc-heading">
12228
+ <code class="highlight language-python"><span class="n">get_computed_fields_grouping</span><span class="p">(</span><span class="n">advanced_ui</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
12229
+
12230
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping" class="headerlink" title="Permanent link">&para;</a></h3>
12231
+
12232
+
12233
+ <div class="doc doc-contents ">
12234
+
12235
+ <p>Return a dictonary of computed fields grouped by the same grouping in the form
12236
+ {
12237
+ <grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
12238
+ ...
12239
+ <grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
12240
+ ...
12241
+ }</p>
12242
+
12243
+ </div>
12244
+
12245
+ </div>
12246
+
12247
+ <div class="doc doc-object doc-function">
12248
+
12249
+
12250
+ <h3 id="nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_advanced" class="doc doc-heading">
12251
+ <code class="highlight language-python"><span class="n">get_computed_fields_grouping_advanced</span><span class="p">()</span></code>
12252
+
12253
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_advanced" class="headerlink" title="Permanent link">&para;</a></h3>
12254
+
12255
+
12256
+ <div class="doc doc-contents ">
12257
+
12258
+ <p>This method exists to help call get_computed_field_groupings() in templates where a function argument (advanced_ui) cannot be specified.
12259
+ Return a dictonary of computed fields grouped by the same grouping in the form
12260
+ {
12261
+ <grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
12262
+ ...
12263
+ <grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
12264
+ ...
12265
+ }
12266
+ which have advanced_ui set to True</p>
12267
+
12268
+ </div>
12269
+
12270
+ </div>
12271
+
12272
+ <div class="doc doc-object doc-function">
12273
+
12274
+
12275
+ <h3 id="nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_basic" class="doc doc-heading">
12276
+ <code class="highlight language-python"><span class="n">get_computed_fields_grouping_basic</span><span class="p">()</span></code>
12277
+
12278
+ <a href="#nautobot.apps.models.CustomFieldModel.get_computed_fields_grouping_basic" class="headerlink" title="Permanent link">&para;</a></h3>
12279
+
12280
+
12281
+ <div class="doc doc-contents ">
12282
+
12283
+ <p>This method exists to help call get_computed_field_groupings() in templates where a function argument (advanced_ui) cannot be specified.
12284
+ Return a dictonary of computed fields grouped by the same grouping in the form
12285
+ {
12286
+ <grouping_1>: [(cf1, <value for cf1>), (cf2, <value for cf2>), ...],
12287
+ ...
12288
+ <grouping_5>: [(cf8, <value for cf8>), (cf9, <value for cf9>), ...],
12289
+ ...
12290
+ }
12291
+ which have advanced_ui set to False</p>
12292
+
12293
+ </div>
12294
+
12295
+ </div>
12296
+
12297
+ <div class="doc doc-object doc-function">
12298
+
12299
+
12173
12300
  <h3 id="nautobot.apps.models.CustomFieldModel.get_custom_field_groupings" class="doc doc-heading">
12174
12301
  <code class="highlight language-python"><span class="n">get_custom_field_groupings</span><span class="p">(</span><span class="n">advanced_ui</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span></code>
12175
12302
 
Binary file