nautobot 2.4.5__py3-none-any.whl → 2.4.6__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.
Files changed (115) hide show
  1. nautobot/core/api/mixins.py +10 -0
  2. nautobot/core/celery/encoders.py +2 -2
  3. nautobot/core/forms/fields.py +21 -5
  4. nautobot/core/forms/utils.py +1 -0
  5. nautobot/core/jobs/bulk_actions.py +1 -1
  6. nautobot/core/management/commands/generate_test_data.py +1 -1
  7. nautobot/core/models/name_color_content_types.py +9 -0
  8. nautobot/core/models/validators.py +7 -0
  9. nautobot/core/settings.py +0 -14
  10. nautobot/core/settings.yaml +0 -28
  11. nautobot/core/tables.py +6 -1
  12. nautobot/core/templates/generic/object_retrieve.html +1 -1
  13. nautobot/core/testing/api.py +18 -0
  14. nautobot/core/tests/nautobot_config.py +0 -2
  15. nautobot/core/tests/runner.py +17 -140
  16. nautobot/core/tests/test_api.py +4 -4
  17. nautobot/core/tests/test_authentication.py +83 -4
  18. nautobot/core/tests/test_forms.py +11 -8
  19. nautobot/core/tests/test_graphql.py +9 -0
  20. nautobot/core/tests/test_jobs.py +7 -0
  21. nautobot/core/ui/object_detail.py +31 -0
  22. nautobot/dcim/factory.py +2 -0
  23. nautobot/dcim/filters/__init__.py +5 -0
  24. nautobot/dcim/forms.py +17 -1
  25. nautobot/dcim/migrations/0068_alter_softwareimagefile_download_url.py +19 -0
  26. nautobot/dcim/migrations/0069_softwareimagefile_external_integration.py +25 -0
  27. nautobot/dcim/models/devices.py +9 -2
  28. nautobot/dcim/tables/devices.py +1 -0
  29. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +4 -0
  30. nautobot/dcim/tests/test_api.py +74 -31
  31. nautobot/dcim/tests/test_filters.py +2 -0
  32. nautobot/dcim/tests/test_models.py +65 -0
  33. nautobot/dcim/tests/test_views.py +3 -0
  34. nautobot/extras/forms/forms.py +7 -3
  35. nautobot/extras/plugins/marketplace_manifest.yml +18 -0
  36. nautobot/extras/tables.py +4 -5
  37. nautobot/extras/templates/extras/inc/panel_changelog.html +1 -1
  38. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  39. nautobot/extras/templates/extras/status.html +1 -37
  40. nautobot/extras/tests/integration/test_notes.py +1 -1
  41. nautobot/extras/tests/test_api.py +22 -7
  42. nautobot/extras/tests/test_changelog.py +4 -4
  43. nautobot/extras/tests/test_customfields.py +3 -0
  44. nautobot/extras/tests/test_plugins.py +19 -13
  45. nautobot/extras/tests/test_relationships.py +9 -0
  46. nautobot/extras/tests/test_tags.py +2 -2
  47. nautobot/extras/tests/test_views.py +15 -6
  48. nautobot/extras/urls.py +1 -30
  49. nautobot/extras/views.py +10 -54
  50. nautobot/ipam/tables.py +6 -2
  51. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -41
  52. nautobot/ipam/templates/ipam/service.html +2 -46
  53. nautobot/ipam/templates/ipam/service_edit.html +1 -17
  54. nautobot/ipam/templates/ipam/service_retrieve.html +7 -0
  55. nautobot/ipam/tests/migration/__init__.py +0 -0
  56. nautobot/ipam/tests/migration/test_migrations.py +510 -0
  57. nautobot/ipam/tests/test_api.py +66 -36
  58. nautobot/ipam/tests/test_filters.py +0 -10
  59. nautobot/ipam/tests/test_views.py +44 -2
  60. nautobot/ipam/urls.py +2 -47
  61. nautobot/ipam/utils/migrations.py +185 -152
  62. nautobot/ipam/utils/testing.py +177 -0
  63. nautobot/ipam/views.py +95 -157
  64. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +47 -0
  65. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -0
  66. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +63 -0
  67. nautobot/project-static/docs/development/apps/api/testing.html +0 -87
  68. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +1 -1
  69. nautobot/project-static/docs/development/core/best-practices.html +3 -3
  70. nautobot/project-static/docs/development/core/getting-started.html +78 -107
  71. nautobot/project-static/docs/development/core/release-checklist.html +1 -1
  72. nautobot/project-static/docs/development/core/style-guide.html +1 -1
  73. nautobot/project-static/docs/development/core/testing.html +24 -198
  74. nautobot/project-static/docs/media/user-guide/administration/getting-started/nautobot-cloud.png +0 -0
  75. nautobot/project-static/docs/objects.inv +0 -0
  76. nautobot/project-static/docs/overview/application_stack.html +1 -1
  77. nautobot/project-static/docs/release-notes/version-2.4.html +226 -1
  78. nautobot/project-static/docs/search/search_index.json +1 -1
  79. nautobot/project-static/docs/sitemap.xml +290 -290
  80. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  81. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -48
  82. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +71 -0
  83. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -1
  84. nautobot/project-static/docs/user-guide/administration/installation/index.html +257 -16
  85. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +1 -1
  86. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -2
  87. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -0
  88. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -11
  89. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +8 -8
  90. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +1 -0
  91. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +40 -25
  92. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -4
  93. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +1 -1
  94. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +77 -5
  95. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +1 -1
  96. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +0 -1
  97. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -1
  98. nautobot/project-static/docs/user-guide/index.html +89 -2
  99. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +207 -122
  100. nautobot/virtualization/forms.py +20 -0
  101. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -39
  102. nautobot/virtualization/templates/virtualization/clustertype.html +1 -0
  103. nautobot/virtualization/tests/test_api.py +14 -3
  104. nautobot/virtualization/tests/test_views.py +10 -2
  105. nautobot/virtualization/urls.py +10 -93
  106. nautobot/virtualization/views.py +33 -72
  107. {nautobot-2.4.5.dist-info → nautobot-2.4.6.dist-info}/METADATA +6 -5
  108. {nautobot-2.4.5.dist-info → nautobot-2.4.6.dist-info}/RECORD +113 -108
  109. {nautobot-2.4.5.dist-info → nautobot-2.4.6.dist-info}/WHEEL +1 -1
  110. nautobot/core/tests/performance_baselines.yml +0 -8900
  111. nautobot/ipam/tests/test_migrations.py +0 -462
  112. /nautobot/ipam/templates/ipam/{namespace_ipaddresses.html → namespace_ip_addresses.html} +0 -0
  113. {nautobot-2.4.5.dist-info → nautobot-2.4.6.dist-info}/LICENSE.txt +0 -0
  114. {nautobot-2.4.5.dist-info → nautobot-2.4.6.dist-info}/NOTICE +0 -0
  115. {nautobot-2.4.5.dist-info → nautobot-2.4.6.dist-info}/entry_points.txt +0 -0
@@ -1,462 +0,0 @@
1
- from unittest import skip, skipIf
2
- import uuid
3
-
4
- from django.db import connection
5
-
6
- from nautobot.core.models.fields import TagsField
7
- from nautobot.core.models.utils import serialize_object
8
- from nautobot.core.testing.migrations import NautobotDataMigrationTest
9
- from nautobot.extras import choices as extras_choices
10
-
11
-
12
- @skip("test skipped until base test can be fixed to handle new migrations")
13
- class AggregateToPrefixMigrationTestCase(NautobotDataMigrationTest):
14
- """Test data migrations removing the Aggregate model and replacing with Prefix in v2.0"""
15
-
16
- migrate_from = [("ipam", "0021_prefix_add_rir_and_date_allocated")]
17
- migrate_to = [("ipam", "0022_aggregate_to_prefix_data_migration")]
18
-
19
- def _create_objectchange(self, instance, change_context_detail):
20
- instance.refresh_from_db()
21
- return self.object_change.objects.create(
22
- action=extras_choices.ObjectChangeActionChoices.ACTION_UPDATE,
23
- change_context=extras_choices.ObjectChangeEventContextChoices.CONTEXT_ORM,
24
- change_context_detail=change_context_detail,
25
- changed_object_id=instance.pk,
26
- changed_object_type=self.content_type.objects.get_for_model(instance.__class__),
27
- object_data=serialize_object(instance),
28
- object_repr="",
29
- request_id=uuid.uuid4(),
30
- )
31
-
32
- def populateDataBeforeMigration(self, installed_apps):
33
- """Populate Aggregate data before migrating to Prefixes"""
34
-
35
- self.aggregate = installed_apps.get_model("ipam", "Aggregate")
36
- # Workaround for django-taggit manager not working in migrations.
37
- # https://github.com/jazzband/django-taggit/issues/101
38
- # https://github.com/jazzband/django-taggit/issues/454
39
- self.aggregate.tags = TagsField()
40
- self.computed_field = installed_apps.get_model("extras", "computedfield")
41
- self.content_type = installed_apps.get_model("contenttypes", "ContentType")
42
- self.custom_field = installed_apps.get_model("extras", "customfield")
43
- self.custom_link = installed_apps.get_model("extras", "customlink")
44
- self.dynamic_group = installed_apps.get_model("extras", "DynamicGroup")
45
- self.note = installed_apps.get_model("extras", "note")
46
- self.object_change = installed_apps.get_model("extras", "objectchange")
47
- self.object_permission = installed_apps.get_model("users", "objectpermission")
48
- self.prefix = installed_apps.get_model("ipam", "prefix")
49
- self.prefix.tags = TagsField()
50
- self.relationship = installed_apps.get_model("extras", "relationship")
51
- self.relationship_association = installed_apps.get_model("extras", "relationshipassociation")
52
- self.rir = installed_apps.get_model("ipam", "RIR")
53
- self.status = installed_apps.get_model("extras", "status")
54
- self.tag = installed_apps.get_model("extras", "tag")
55
-
56
- self.aggregate_ct = self.content_type.objects.get_for_model(self.aggregate)
57
- self.prefix_ct = self.content_type.objects.get_for_model(self.prefix)
58
-
59
- self.prefix_status = self.status.objects.create(name="Active")
60
- self.prefix_status.content_types.add(self.prefix_ct)
61
-
62
- self.rir1 = self.rir.objects.create(name="RFC1918", is_private=True)
63
- self.rir2 = self.rir.objects.create(name="ARIN")
64
-
65
- # Create 4 prefixes that will be merged into by Aggregates with duplicate network/prefix_length
66
- self.prefix1 = self.prefix.objects.create(
67
- network="10.1.0.0", prefix_length=24, status=self.prefix_status, description="PrefixDesc"
68
- )
69
- self.prefix2 = self.prefix.objects.create(
70
- network="10.2.0.0", prefix_length=25, status=self.prefix_status, description="PrefixDesc"
71
- )
72
- self.prefix3 = self.prefix.objects.create(network="10.3.0.0", prefix_length=26, status=self.prefix_status)
73
- self.prefix4 = self.prefix.objects.create(network="10.4.0.0", prefix_length=27, status=self.prefix_status)
74
- self.aggregate1 = self.aggregate.objects.create(network="10.1.0.0", rir=self.rir1, prefix_length=24)
75
- self.aggregate2 = self.aggregate.objects.create(network="10.2.0.0", rir=self.rir1, prefix_length=25)
76
- self.aggregate3 = self.aggregate.objects.create(
77
- network="10.3.0.0", rir=self.rir1, prefix_length=26, description="AggregateDesc"
78
- )
79
- self.aggregate4 = self.aggregate.objects.create(
80
- network="10.4.0.0", rir=self.rir1, prefix_length=27, description="AggregateDesc"
81
- )
82
-
83
- # Create 8 prefixes that are not duplicated by Aggregates and will not be touched by migration
84
- # self.prefix5(10.5.0.0)
85
- # ...
86
- # self.prefix12(10.12.0.0)
87
- for i in range(8):
88
- prefix = self.prefix.objects.create(network=f"10.{i+5}.0.0", prefix_length=28, status=self.prefix_status)
89
- setattr(self, f"prefix{i+5}", prefix)
90
-
91
- # Create 16 aggregates that will be migrated to new Prefixes
92
- # self.aggregate5(8.5.0.0)
93
- # ...
94
- # self.aggregate20(8.20.0.0)
95
- for i in range(16):
96
- aggregate = self.aggregate.objects.create(
97
- network=f"8.{i+5}.0.0", rir=self.rir2, prefix_length=29, description="AggregateDesc"
98
- )
99
- setattr(self, f"aggregate{i+5}", aggregate)
100
-
101
- # tags
102
- self.prefix_tag_a = self.tag.objects.create(name="PrefixTagA")
103
- self.prefix_tag_b = self.tag.objects.create(name="PrefixTagB")
104
- self.prefix_tag_a.content_types.add(self.prefix_ct)
105
- self.prefix_tag_b.content_types.add(self.prefix_ct)
106
- self.aggregate_tag_a = self.tag.objects.create(name="AggregateTagA")
107
- self.aggregate_tag_b = self.tag.objects.create(name="AggregateTagB")
108
- self.aggregate_tag_a.content_types.add(self.aggregate_ct)
109
- self.aggregate_tag_b.content_types.add(self.aggregate_ct)
110
- self.prefix1.tags.add("PrefixTagA")
111
- self.prefix2.tags.add("PrefixTagA")
112
- self.prefix3.tags.add("PrefixTagA")
113
- self.prefix4.tags.add("PrefixTagA")
114
- self.aggregate1.tags.add("AggregateTagA", "AggregateTagB")
115
- self.aggregate2.tags.add("AggregateTagA")
116
- self.aggregate3.tags.add("AggregateTagB")
117
- self.aggregate5.tags.add("AggregateTagA", "AggregateTagB") # pylint: disable=no-member
118
- self.aggregate6.tags.add("AggregateTagB") # pylint: disable=no-member
119
-
120
- # notes
121
- self.note.objects.create(
122
- note="Prefix1 test note",
123
- assigned_object_type=self.prefix_ct,
124
- assigned_object_id=self.prefix1.id,
125
- )
126
- self.note.objects.create(
127
- note="Prefix2 test note",
128
- assigned_object_type=self.prefix_ct,
129
- assigned_object_id=self.prefix2.id,
130
- )
131
- self.note.objects.create(
132
- note="Aggregate1 test note",
133
- assigned_object_type=self.aggregate_ct,
134
- assigned_object_id=self.aggregate1.id,
135
- )
136
- self.note.objects.create(
137
- note="Aggregate3 test note",
138
- assigned_object_type=self.aggregate_ct,
139
- assigned_object_id=self.aggregate3.id,
140
- )
141
- self.note.objects.create(
142
- note="Aggregate5 test note",
143
- assigned_object_type=self.aggregate_ct,
144
- assigned_object_id=self.aggregate5.id, # pylint: disable=no-member
145
- )
146
-
147
- # object permissions
148
- object_permission1 = self.object_permission.objects.create(
149
- name="Aggregate permission 1", actions=["view", "add", "change", "delete"]
150
- )
151
- object_permission2 = self.object_permission.objects.create(
152
- name="Aggregate permission 2", actions=["add", "delete"], enabled=False
153
- )
154
- object_permission1.object_types.add(self.aggregate_ct)
155
- object_permission2.object_types.add(self.aggregate_ct)
156
-
157
- # object changes
158
- self._create_objectchange(self.prefix1, "Pre-migration object change for prefix1")
159
- self._create_objectchange(self.prefix4, "Pre-migration object change for prefix4")
160
- self._create_objectchange(self.prefix5, "Pre-migration object change for prefix5") # pylint: disable=no-member
161
- self._create_objectchange(self.aggregate5, "Pre-migration object change for aggregate5") # pylint: disable=no-member
162
-
163
- # custom fields
164
- prefix_cf1 = self.custom_field.objects.create(name="prefixcf1")
165
- prefix_cf1.content_types.add(self.prefix_ct)
166
- aggregate_cf1 = self.custom_field.objects.create(name="aggregatecf1")
167
- aggregate_cf1.content_types.add(self.aggregate_ct)
168
- prefixaggregate_cf1 = self.custom_field.objects.create(name="prefixaggregatecf1")
169
- prefixaggregate_cf1.content_types.add(self.aggregate_ct, self.prefix_ct)
170
-
171
- self.prefix1._custom_field_data["prefixcf1"] = "testdata prefixcf1 prefix1"
172
- self.prefix1._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 prefix1"
173
- self.aggregate1._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate1"
174
-
175
- self.prefix2._custom_field_data["prefixcf1"] = "testdata prefixcf1 prefix2"
176
- self.prefix2._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 prefix2"
177
- self.aggregate2._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate2"
178
- self.aggregate2._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate2"
179
-
180
- self.aggregate3._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate3"
181
-
182
- self.prefix5._custom_field_data["prefixcf1"] = "testdata prefixcf1 prefix5" # pylint: disable=no-member
183
- self.prefix5._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 prefix5" # pylint: disable=no-member
184
-
185
- self.aggregate5._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate5" # pylint: disable=no-member
186
- self.aggregate5._custom_field_data["aggregatecf1"] = "testdata aggregatecf1 aggregate5" # pylint: disable=no-member
187
-
188
- self.aggregate6._custom_field_data["prefixaggregatecf1"] = "testdata prefixaggregatecf1 aggregate6" # pylint: disable=no-member
189
-
190
- self.prefix1.save()
191
- self.prefix2.save()
192
- self.prefix3.save()
193
- self.prefix4.save()
194
- self.prefix5.save() # pylint: disable=no-member
195
- self.aggregate1.save()
196
- self.aggregate2.save()
197
- self.aggregate3.save()
198
- self.aggregate5.save() # pylint: disable=no-member
199
- self.aggregate6.save() # pylint: disable=no-member
200
-
201
- @skipIf(
202
- connection.vendor != "postgresql",
203
- "mysql does not support rollbacks",
204
- )
205
- def test_aggregate_to_prefix_migration_object_count(self):
206
- with self.subTest("Test Prefix count"):
207
- self.assertEqual(self.prefix.objects.count(), 28)
208
- with self.subTest("Test Aggregate count"):
209
- self.assertEqual(self.aggregate.objects.count(), 20)
210
-
211
- @skipIf(
212
- connection.vendor != "postgresql",
213
- "mysql does not support rollbacks",
214
- )
215
- def test_aggregate_to_prefix_migration_network(self):
216
- for i in range(16):
217
- self.assertTrue(
218
- self.prefix.objects.filter(network=f"8.{i+5}.0.0", prefix_length=29, rir=self.rir2).exists()
219
- )
220
-
221
- @skipIf(
222
- connection.vendor != "postgresql",
223
- "mysql does not support rollbacks",
224
- )
225
- def test_aggregate_to_prefix_migration_rir(self):
226
- with self.subTest(f"prefix.rir = {self.rir1.name}"):
227
- self.assertEqual(self.prefix.objects.get(network="10.1.0.0").rir, self.rir1)
228
- self.assertEqual(self.prefix.objects.get(network="10.2.0.0").rir, self.rir1)
229
- self.assertEqual(self.prefix.objects.get(network="10.3.0.0").rir, self.rir1)
230
- self.assertEqual(self.prefix.objects.get(network="10.4.0.0").rir, self.rir1)
231
- with self.subTest(f"prefix.rir = {self.rir2.name}"):
232
- for i in range(16):
233
- prefix = self.prefix.objects.get(network=f"8.{i+5}.0.0")
234
- self.assertEqual(prefix.rir, self.rir2)
235
- with self.subTest("prefix.rir is None"):
236
- for i in range(8):
237
- prefix = self.prefix.objects.get(network=f"10.{i+5}.0.0")
238
- self.assertIsNone(prefix.rir)
239
-
240
- @skipIf(
241
- connection.vendor != "postgresql",
242
- "mysql does not support rollbacks",
243
- )
244
- def test_aggregate_to_prefix_migration_description(self):
245
- self.assertEqual(self.prefix.objects.filter(description="PrefixDesc").count(), 2)
246
- self.assertEqual(self.prefix.objects.filter(description="").count(), 10)
247
- self.assertEqual(self.prefix.objects.filter(description="AggregateDesc").count(), 16)
248
-
249
- @skipIf(
250
- connection.vendor != "postgresql",
251
- "mysql does not support rollbacks",
252
- )
253
- def test_aggregate_to_prefix_migration_status(self):
254
- self.assertEqual(self.prefix.objects.filter(status=self.prefix_status).count(), self.prefix.objects.count())
255
-
256
- @skipIf(
257
- connection.vendor != "postgresql",
258
- "mysql does not support rollbacks",
259
- )
260
- def test_aggregate_to_prefix_migration_tags(self):
261
- with self.subTest("Prefix content type was added to Aggregate Tags"):
262
- prefix_tags = self.tag.objects.filter(content_types=self.prefix_ct)
263
- self.assertIn(self.aggregate_tag_a, prefix_tags)
264
- self.assertIn(self.aggregate_tag_b, prefix_tags)
265
-
266
- # assert that tags were migrated to new prefix instances
267
- # compare list of PKs since tag managers don't work in migrations
268
- self.assertCountEqual(
269
- self.prefix.objects.get(network="10.1.0.0").tags.values_list("id", flat=True),
270
- self.tag.objects.filter(name__in=["PrefixTagA", "AggregateTagA", "AggregateTagB"]).values_list(
271
- "id", flat=True
272
- ),
273
- )
274
- self.assertCountEqual(
275
- self.prefix.objects.get(network="10.2.0.0").tags.values_list("id", flat=True),
276
- self.tag.objects.filter(name__in=["PrefixTagA", "AggregateTagA"]).values_list("id", flat=True),
277
- )
278
- self.assertCountEqual(
279
- self.prefix.objects.get(network="10.3.0.0").tags.values_list("id", flat=True),
280
- self.tag.objects.filter(name__in=["PrefixTagA", "AggregateTagB"]).values_list("id", flat=True),
281
- )
282
- self.assertCountEqual(
283
- self.prefix.objects.get(network="10.4.0.0").tags.values_list("id", flat=True),
284
- self.tag.objects.filter(name="PrefixTagA").values_list("id", flat=True),
285
- )
286
- self.assertCountEqual(
287
- self.prefix.objects.get(network="8.5.0.0").tags.values_list("id", flat=True),
288
- self.tag.objects.filter(name__in=["AggregateTagA", "AggregateTagB"]).values_list("id", flat=True),
289
- )
290
- self.assertCountEqual(
291
- self.prefix.objects.get(network="8.6.0.0").tags.values_list("id", flat=True),
292
- self.tag.objects.filter(name="AggregateTagB").values_list("id", flat=True),
293
- )
294
- for i in range(7, 21):
295
- prefix = self.prefix.objects.get(network=f"8.{i}.0.0")
296
- self.assertCountEqual(prefix.tags.values_list("id", flat=True), [])
297
-
298
- @skipIf(
299
- connection.vendor != "postgresql",
300
- "mysql does not support rollbacks",
301
- )
302
- def test_aggregate_to_prefix_migration_notes(self):
303
- # no notes are assigned to aggregates
304
- self.assertQuerysetEqual(
305
- self.note.objects.filter(assigned_object_type=self.aggregate_ct), self.note.objects.none()
306
- )
307
- # no extra notes were created
308
- self.assertEqual(self.note.objects.count(), 5)
309
-
310
- # aggregate1 note added on top of existing note on prefix1
311
- self.assertQuerysetEqual(
312
- self.note.objects.filter(assigned_object_type=self.prefix_ct, assigned_object_id=self.prefix1.id),
313
- self.note.objects.filter(note__in=["Prefix1 test note", "Aggregate1 test note"]),
314
- )
315
-
316
- # prefix2 note was unchanged
317
- self.assertQuerysetEqual(
318
- self.note.objects.filter(assigned_object_type=self.prefix_ct, assigned_object_id=self.prefix2.id),
319
- self.note.objects.filter(note="Prefix2 test note"),
320
- )
321
-
322
- # aggregate3 note was migrated to prefix3
323
- self.assertQuerysetEqual(
324
- self.note.objects.filter(assigned_object_type=self.prefix_ct, assigned_object_id=self.prefix3.id),
325
- self.note.objects.filter(note="Aggregate3 test note"),
326
- )
327
-
328
- # no notes for prefix4
329
- self.assertQuerysetEqual(
330
- self.note.objects.filter(assigned_object_type=self.prefix_ct, assigned_object_id=self.prefix4.id),
331
- self.note.objects.none(),
332
- )
333
-
334
- # aggregate5 note was migrated to new prefix object
335
- aggregate5_migrated_prefix = self.prefix.objects.get(network="8.5.0.0")
336
- self.assertQuerysetEqual(
337
- self.note.objects.filter(
338
- assigned_object_type=self.prefix_ct,
339
- assigned_object_id=aggregate5_migrated_prefix.id,
340
- ),
341
- self.note.objects.filter(note="Aggregate5 test note"),
342
- )
343
-
344
- # no other notes are related to remaining prefixes
345
- for i in range(5, 13):
346
- prefix = self.prefix.objects.get(network=f"10.{i}.0.0")
347
- self.assertQuerysetEqual(
348
- self.note.objects.filter(assigned_object_type=self.prefix_ct, assigned_object_id=prefix.id),
349
- self.note.objects.none(),
350
- )
351
- for i in range(6, 21):
352
- prefix = self.prefix.objects.get(network=f"8.{i}.0.0")
353
- self.assertQuerysetEqual(
354
- self.note.objects.filter(assigned_object_type=self.prefix_ct, assigned_object_id=prefix.id),
355
- self.note.objects.none(),
356
- )
357
-
358
- @skipIf(
359
- connection.vendor != "postgresql",
360
- "mysql does not support rollbacks",
361
- )
362
- def test_aggregate_to_prefix_migration_permissions(self):
363
- self.assertEqual(self.object_permission.objects.count(), 2)
364
-
365
- # assert prefix content type was added to object permission 1
366
- object_permission1 = self.object_permission.objects.filter(
367
- name="Aggregate permission 1", actions=["view", "add", "change", "delete"]
368
- )
369
- self.assertTrue(object_permission1.exists())
370
- self.assertTrue(object_permission1.first().object_types.filter(id=self.prefix_ct.id).exists())
371
-
372
- # assert prefix content type was added to object permission 2
373
- object_permission2 = self.object_permission.objects.filter(
374
- name="Aggregate permission 2", actions=["add", "delete"], enabled=False
375
- )
376
- self.assertTrue(object_permission2.exists())
377
- self.assertTrue(object_permission2.first().object_types.filter(id=self.prefix_ct.id).exists())
378
-
379
- @skipIf(
380
- connection.vendor != "postgresql",
381
- "mysql does not support rollbacks",
382
- )
383
- def test_aggregate_to_prefix_migration_object_changes(self):
384
- self.assertEqual(self.object_change.objects.filter(changed_object_type=self.prefix_ct).count(), 24)
385
- self.assertEqual(self.object_change.objects.filter(changed_object_type=self.aggregate_ct).count(), 0)
386
-
387
- for prefix in (self.prefix1, self.prefix4, self.prefix.objects.get(network="8.5.0.0")):
388
- self.assertEqual(
389
- self.object_change.objects.filter(changed_object_id=prefix.id).count(),
390
- 2,
391
- )
392
-
393
- for prefix in (self.prefix2, self.prefix3, self.prefix5): # pylint: disable=no-member
394
- self.assertEqual(
395
- self.object_change.objects.filter(changed_object_id=prefix.id).count(),
396
- 1,
397
- )
398
-
399
- for i in range(6, 13):
400
- prefix = self.prefix.objects.get(network=f"10.{i}.0.0")
401
- self.assertEqual(
402
- self.object_change.objects.filter(changed_object_id=prefix.id).count(),
403
- 0,
404
- )
405
-
406
- for i in range(6, 21):
407
- prefix = self.prefix.objects.get(network=f"8.{i}.0.0")
408
- self.assertEqual(
409
- self.object_change.objects.filter(changed_object_id=prefix.id).count(),
410
- 1,
411
- )
412
-
413
- @skipIf(
414
- connection.vendor != "postgresql",
415
- "mysql does not support rollbacks",
416
- )
417
- def test_aggregate_to_prefix_migration_custom_fields(self):
418
- # This change is necessary because name attribute is not specified now in example_app's signal.py
419
- self.assertEqual(self.custom_field.objects.exclude(name="").count(), 3)
420
- self.assertEqual(self.custom_field.objects.filter(content_types=self.prefix_ct).count(), 3)
421
- self.assertEqual(self.custom_field.objects.filter(content_types=self.aggregate_ct).count(), 2)
422
-
423
- expected = {
424
- "prefix1": {
425
- "prefixcf1": "testdata prefixcf1 prefix1",
426
- "prefixaggregatecf1": "testdata prefixaggregatecf1 prefix1",
427
- "aggregatecf1": "testdata aggregatecf1 aggregate1",
428
- },
429
- "prefix2": {
430
- "prefixcf1": "testdata prefixcf1 prefix2",
431
- "prefixaggregatecf1": "testdata prefixaggregatecf1 prefix2",
432
- "aggregatecf1": "testdata aggregatecf1 aggregate2",
433
- },
434
- "prefix3": {
435
- "aggregatecf1": "testdata aggregatecf1 aggregate3",
436
- },
437
- "prefix4": {},
438
- "prefix5": {
439
- "prefixcf1": "testdata prefixcf1 prefix5",
440
- "prefixaggregatecf1": "testdata prefixaggregatecf1 prefix5",
441
- },
442
- }
443
-
444
- for i in range(1, 6):
445
- with self.subTest(f"Custom fields for prefix{i}"):
446
- prefix = self.prefix.objects.get(network=f"10.{i}.0.0")
447
- self.assertDictEqual(prefix._custom_field_data, expected[f"prefix{i}"])
448
-
449
- with self.subTest("Custom fields for prefix 8.5.0.0"):
450
- expected = {
451
- "prefixaggregatecf1": "testdata prefixaggregatecf1 aggregate5",
452
- "aggregatecf1": "testdata aggregatecf1 aggregate5",
453
- }
454
- prefix = self.prefix.objects.get(network="8.5.0.0")
455
- self.assertDictEqual(prefix._custom_field_data, expected)
456
-
457
- with self.subTest("Custom fields for prefix 8.6.0.0"):
458
- expected = {
459
- "prefixaggregatecf1": "testdata prefixaggregatecf1 aggregate6",
460
- }
461
- prefix = self.prefix.objects.get(network="8.6.0.0")
462
- self.assertDictEqual(prefix._custom_field_data, expected)