Просмотр исходного кода

Revert "Closes #13647: Squash all migrations prior to v3.7 (#14853)"

This reverts commit 874685fd6f6b463e68fd2eef4711aa6765371a17.
Jeremy Stretch 2 лет назад
Родитель
Сommit
222388b988
100 измененных файлов с 5959 добавлено и 3153 удалено
  1. 2 0
      netbox/account/migrations/0001_initial.py
  2. 31 53
      netbox/circuits/migrations/0001_squashed.py
  3. 60 34
      netbox/circuits/migrations/0002_squashed_0029.py
  4. 20 0
      netbox/circuits/migrations/0003_extend_tag_support.py
  5. 0 73
      netbox/circuits/migrations/0003_squashed.py
  6. 21 0
      netbox/circuits/migrations/0004_rename_cable_peer.py
  7. 17 0
      netbox/circuits/migrations/0032_provider_service_id.py
  8. 44 0
      netbox/circuits/migrations/0033_standardize_id_fields.py
  9. 38 0
      netbox/circuits/migrations/0034_created_datetimefield.py
  10. 19 0
      netbox/circuits/migrations/0035_provider_asns.py
  11. 28 0
      netbox/circuits/migrations/0036_circuit_termination_date_tags_custom_fields.py
  12. 16 0
      netbox/circuits/migrations/0037_new_cabling_models.py
  13. 20 0
      netbox/circuits/migrations/0038_cabling_cleanup.py
  14. 39 0
      netbox/circuits/migrations/0039_unique_constraints.py
  15. 59 0
      netbox/circuits/migrations/0040_provider_remove_deprecated_fields.py
  16. 18 0
      netbox/circuits/migrations/0041_standardize_description_comments.py
  17. 91 0
      netbox/circuits/migrations/0042_provideraccount.py
  18. 2 0
      netbox/circuits/migrations/0043_circuittype_color.py
  19. 33 64
      netbox/core/migrations/0001_initial.py
  20. 40 0
      netbox/core/migrations/0002_managedfile.py
  21. 0 68
      netbox/core/migrations/0002_squashed.py
  22. 39 0
      netbox/core/migrations/0003_job.py
  23. 46 0
      netbox/core/migrations/0004_replicate_jobresults.py
  24. 18 0
      netbox/core/migrations/0005_job_created_auto_now.py
  25. 2 0
      netbox/core/migrations/0006_datasource_type_remove_choices.py
  26. 0 979
      netbox/dcim/migrations/0001_initial.py
  27. 664 0
      netbox/dcim/migrations/0001_squashed.py
  28. 288 11
      netbox/dcim/migrations/0002_squashed.py
  29. 0 1190
      netbox/dcim/migrations/0003_squashed.py
  30. 485 0
      netbox/dcim/migrations/0003_squashed_0130.py
  31. 21 0
      netbox/dcim/migrations/0131_consoleport_speed.py
  32. 16 0
      netbox/dcim/migrations/0132_cable_length.py
  33. 32 0
      netbox/dcim/migrations/0133_port_colors.py
  34. 23 0
      netbox/dcim/migrations/0134_interface_wwn_bridge.py
  35. 23 0
      netbox/dcim/migrations/0135_tenancy_extensions.py
  36. 21 0
      netbox/dcim/migrations/0136_device_airflow.py
  37. 83 0
      netbox/dcim/migrations/0137_relax_uniqueness_constraints.py
  38. 50 0
      netbox/dcim/migrations/0138_extend_tag_support.py
  39. 91 0
      netbox/dcim/migrations/0139_rename_cable_peer.py
  40. 49 0
      netbox/dcim/migrations/0140_wireless.py
  41. 19 0
      netbox/dcim/migrations/0141_asn_model.py
  42. 29 0
      netbox/dcim/migrations/0142_rename_128gfc_qsfp28.py
  43. 23 0
      netbox/dcim/migrations/0143_remove_primary_for_related_name.py
  44. 31 0
      netbox/dcim/migrations/0144_fix_cable_abs_length.py
  45. 59 0
      netbox/dcim/migrations/0145_site_remove_deprecated_fields.py
  46. 279 0
      netbox/dcim/migrations/0146_modules.py
  47. 38 0
      netbox/dcim/migrations/0147_inventoryitemrole.py
  48. 23 0
      netbox/dcim/migrations/0148_inventoryitem_component.py
  49. 43 0
      netbox/dcim/migrations/0149_inventoryitem_templates.py
  50. 20 0
      netbox/dcim/migrations/0150_interface_vrf.py
  51. 23 0
      netbox/dcim/migrations/0151_interface_speed_duplex.py
  52. 274 0
      netbox/dcim/migrations/0152_standardize_id_fields.py
  53. 208 0
      netbox/dcim/migrations/0153_created_datetimefield.py
  54. 23 0
      netbox/dcim/migrations/0154_half_height_rack_units.py
  55. 33 0
      netbox/dcim/migrations/0155_interface_poe_mode_type.py
  56. 18 0
      netbox/dcim/migrations/0156_location_status.py
  57. 95 0
      netbox/dcim/migrations/0157_new_cabling_models.py
  58. 87 0
      netbox/dcim/migrations/0158_populate_cable_terminations.py
  59. 50 0
      netbox/dcim/migrations/0159_populate_cable_paths.py
  60. 46 0
      netbox/dcim/migrations/0160_populate_cable_ends.py
  61. 134 0
      netbox/dcim/migrations/0161_cabling_cleanup.py
  62. 332 0
      netbox/dcim/migrations/0162_unique_constraints.py
  63. 72 0
      netbox/dcim/migrations/0163_weight_fields.py
  64. 18 0
      netbox/dcim/migrations/0164_rack_mounting_depth.py
  65. 78 0
      netbox/dcim/migrations/0165_standardize_description_comments.py
  66. 54 0
      netbox/dcim/migrations/0166_virtualdevicecontext.py
  67. 18 0
      netbox/dcim/migrations/0167_module_status.py
  68. 22 0
      netbox/dcim/migrations/0168_interface_template_enabled.py
  69. 19 0
      netbox/dcim/migrations/0169_devicetype_default_platform.py
  70. 28 0
      netbox/dcim/migrations/0170_configtemplate.py
  71. 21 0
      netbox/dcim/migrations/0171_cabletermination_change_logging.py
  72. 42 0
      netbox/dcim/migrations/0172_larger_power_draw_values.py
  73. 19 0
      netbox/dcim/migrations/0173_remove_napalm_fields.py
  74. 22 0
      netbox/dcim/migrations/0174_device_latitude_device_longitude.py
  75. 18 0
      netbox/dcim/migrations/0174_rack_starting_unit.py
  76. 25 0
      netbox/dcim/migrations/0175_device_oob_ip.py
  77. 83 0
      netbox/dcim/migrations/0176_device_component_counters.py
  78. 83 0
      netbox/dcim/migrations/0177_devicetype_component_counters.py
  79. 31 0
      netbox/dcim/migrations/0178_virtual_chassis_member_counter.py
  80. 18 0
      netbox/dcim/migrations/0179_interfacetemplate_rf_role.py
  81. 20 0
      netbox/dcim/migrations/0180_powerfeed_tenant.py
  82. 35 0
      netbox/dcim/migrations/0181_rename_device_role_device_role.py
  83. 22 0
      netbox/dcim/migrations/0182_zero_length_cable_fix.py
  84. 0 453
      netbox/extras/migrations/0001_initial.py
  85. 235 0
      netbox/extras/migrations/0001_squashed.py
  86. 0 228
      netbox/extras/migrations/0002_squashed.py
  87. 142 0
      netbox/extras/migrations/0002_squashed_0059.py
  88. 16 0
      netbox/extras/migrations/0060_customlink_button_class.py
  89. 51 0
      netbox/extras/migrations/0061_extras_change_logging.py
  90. 26 0
      netbox/extras/migrations/0062_clear_secrets_changelog.py
  91. 18 0
      netbox/extras/migrations/0063_webhook_conditions.py
  92. 20 0
      netbox/extras/migrations/0064_configrevision.py
  93. 16 0
      netbox/extras/migrations/0065_imageattachment_change_logging.py
  94. 34 0
      netbox/extras/migrations/0066_customfield_name_validation.py
  95. 21 0
      netbox/extras/migrations/0067_customfield_min_max_values.py
  96. 18 0
      netbox/extras/migrations/0068_configcontext_cluster_types.py
  97. 18 0
      netbox/extras/migrations/0069_custom_object_field.py
  98. 18 0
      netbox/extras/migrations/0070_customlink_enabled.py
  99. 89 0
      netbox/extras/migrations/0071_standardize_id_fields.py
  100. 53 0
      netbox/extras/migrations/0072_created_datetimefield.py

+ 2 - 0
netbox/account/migrations/0001_initial.py

@@ -1,3 +1,5 @@
+# Generated by Django 4.1.10 on 2023-07-30 17:49
+
 from django.db import migrations
 
 

+ 31 - 53
netbox/circuits/migrations/0001_initial.py → netbox/circuits/migrations/0001_squashed.py

@@ -1,6 +1,7 @@
+import ipam.fields
+from utilities.json import CustomFieldJSONEncoder
 from django.db import migrations, models
 import django.db.models.deletion
-import utilities.json
 
 
 class Migration(migrations.Migration):
@@ -10,36 +11,36 @@ class Migration(migrations.Migration):
     dependencies = [
     ]
 
+    replaces = [
+        ('circuits', '0001_initial'),
+    ]
+
     operations = [
         migrations.CreateModel(
             name='Circuit',
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('created', models.DateField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
                 ('cid', models.CharField(max_length=100)),
                 ('status', models.CharField(default='active', max_length=50)),
                 ('install_date', models.DateField(blank=True, null=True)),
-                ('termination_date', models.DateField(blank=True, null=True)),
                 ('commit_rate', models.PositiveIntegerField(blank=True, null=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('comments', models.TextField(blank=True)),
             ],
             options={
-                'verbose_name': 'circuit',
-                'verbose_name_plural': 'circuits',
-                'ordering': ['provider', 'provider_account', 'cid'],
+                'ordering': ['provider', 'cid'],
             },
         ),
         migrations.CreateModel(
             name='CircuitTermination',
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('created', models.DateField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
                 ('mark_connected', models.BooleanField(default=False)),
                 ('term_side', models.CharField(max_length=1)),
                 ('port_speed', models.PositiveIntegerField(blank=True, null=True)),
@@ -49,80 +50,57 @@ class Migration(migrations.Migration):
                 ('description', models.CharField(blank=True, max_length=200)),
             ],
             options={
-                'verbose_name': 'circuit termination',
-                'verbose_name_plural': 'circuit terminations',
                 'ordering': ['circuit', 'term_side'],
             },
         ),
         migrations.CreateModel(
             name='CircuitType',
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('created', models.DateField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
                 ('name', models.CharField(max_length=100, unique=True)),
                 ('slug', models.SlugField(max_length=100, unique=True)),
                 ('description', models.CharField(blank=True, max_length=200)),
             ],
             options={
-                'verbose_name': 'circuit type',
-                'verbose_name_plural': 'circuit types',
                 'ordering': ('name',),
             },
         ),
         migrations.CreateModel(
             name='Provider',
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('created', models.DateField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
                 ('name', models.CharField(max_length=100, unique=True)),
                 ('slug', models.SlugField(max_length=100, unique=True)),
-            ],
-            options={
-                'verbose_name': 'provider',
-                'verbose_name_plural': 'providers',
-                'ordering': ['name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='ProviderAccount',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
+                ('asn', ipam.fields.ASNField(blank=True, null=True)),
+                ('account', models.CharField(blank=True, max_length=30)),
+                ('portal_url', models.URLField(blank=True)),
+                ('noc_contact', models.TextField(blank=True)),
+                ('admin_contact', models.TextField(blank=True)),
                 ('comments', models.TextField(blank=True)),
-                ('account', models.CharField(max_length=100)),
-                ('name', models.CharField(blank=True, max_length=100)),
             ],
             options={
-                'verbose_name': 'provider account',
-                'verbose_name_plural': 'provider accounts',
-                'ordering': ('provider', 'account'),
+                'ordering': ['name'],
             },
         ),
         migrations.CreateModel(
             name='ProviderNetwork',
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('created', models.DateField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100)),
                 ('description', models.CharField(blank=True, max_length=200)),
                 ('comments', models.TextField(blank=True)),
-                ('name', models.CharField(max_length=100)),
-                ('service_id', models.CharField(blank=True, max_length=100)),
                 ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='networks', to='circuits.provider')),
             ],
             options={
-                'verbose_name': 'provider network',
-                'verbose_name_plural': 'provider networks',
                 'ordering': ('provider', 'name'),
             },
         ),

+ 60 - 34
netbox/circuits/migrations/0004_squashed.py → netbox/circuits/migrations/0002_squashed_0029.py

@@ -1,13 +1,15 @@
 from django.db import migrations, models
 import django.db.models.deletion
+import taggit.managers
 
 
 class Migration(migrations.Migration):
 
-    initial = True
-
     dependencies = [
-        ('circuits', '0003_squashed'),
+        ('dcim', '0001_initial'),
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('circuits', '0001_initial'),
+        ('extras', '0001_initial'),
         ('tenancy', '0001_initial'),
     ]
 
@@ -40,22 +42,54 @@ class Migration(migrations.Migration):
         ('circuits', '0027_providernetwork'),
         ('circuits', '0028_cache_circuit_terminations'),
         ('circuits', '0029_circuit_tracing'),
-        ('circuits', '0003_extend_tag_support'),  # Misnumbered
-        ('circuits', '0004_rename_cable_peer'),   # Misnumbered
-        ('circuits', '0032_provider_service_id'),
-        ('circuits', '0033_standardize_id_fields'),
-        ('circuits', '0034_created_datetimefield'),
-        ('circuits', '0035_provider_asns'),
-        ('circuits', '0036_circuit_termination_date_tags_custom_fields'),
-        ('circuits', '0037_new_cabling_models'),
-        ('circuits', '0038_cabling_cleanup'),
-        ('circuits', '0039_unique_constraints'),
-        ('circuits', '0040_provider_remove_deprecated_fields'),
-        ('circuits', '0041_standardize_description_comments'),
-        ('circuits', '0042_provideraccount'),
     ]
 
     operations = [
+        migrations.AddField(
+            model_name='providernetwork',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='provider',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='circuit',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='circuits.circuit'),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='provider_network',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='circuits.providernetwork'),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='site',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.site'),
+        ),
+        migrations.AddField(
+            model_name='circuit',
+            name='provider',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provider'),
+        ),
+        migrations.AddField(
+            model_name='circuit',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
         migrations.AddField(
             model_name='circuit',
             name='tenant',
@@ -78,26 +112,18 @@ class Migration(migrations.Migration):
         ),
         migrations.AddConstraint(
             model_name='providernetwork',
-            constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='provideraccount',
-            constraint=models.UniqueConstraint(fields=('provider', 'account'), name='circuits_provideraccount_unique_provider_account'),
+            constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_provider_name'),
         ),
-        migrations.AddConstraint(
-            model_name='provideraccount',
-            constraint=models.UniqueConstraint(condition=models.Q(('name', ''), _negated=True), fields=('provider', 'name'), name='circuits_provideraccount_unique_provider_name'),
+        migrations.AlterUniqueTogether(
+            name='providernetwork',
+            unique_together={('provider', 'name')},
         ),
-        migrations.AddConstraint(
-            model_name='circuittermination',
-            constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'),
+        migrations.AlterUniqueTogether(
+            name='circuittermination',
+            unique_together={('circuit', 'term_side')},
         ),
-        migrations.AddConstraint(
-            model_name='circuit',
-            constraint=models.UniqueConstraint(fields=('provider', 'cid'), name='circuits_circuit_unique_provider_cid'),
-        ),
-        migrations.AddConstraint(
-            model_name='circuit',
-            constraint=models.UniqueConstraint(fields=('provider_account', 'cid'), name='circuits_circuit_unique_provideraccount_cid'),
+        migrations.AlterUniqueTogether(
+            name='circuit',
+            unique_together={('provider', 'cid')},
         ),
     ]

+ 20 - 0
netbox/circuits/migrations/0003_extend_tag_support.py

@@ -0,0 +1,20 @@
+# Generated by Django 3.2.8 on 2021-10-21 14:50
+
+from django.db import migrations
+import taggit.managers
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0062_clear_secrets_changelog'),
+        ('circuits', '0002_squashed_0029'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='circuittype',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+    ]

+ 0 - 73
netbox/circuits/migrations/0003_squashed.py

@@ -1,73 +0,0 @@
-from django.db import migrations, models
-import django.db.models.deletion
-import taggit.managers
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('extras', '0001_initial'),
-        ('ipam', '0001_initial'),
-        ('dcim', '0001_initial'),
-        ('circuits', '0002_squashed'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='provider',
-            name='asns',
-            field=models.ManyToManyField(blank=True, related_name='providers', to='ipam.asn'),
-        ),
-        migrations.AddField(
-            model_name='provider',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='circuittype',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='circuittermination',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='circuittermination',
-            name='circuit',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='circuits.circuit'),
-        ),
-        migrations.AddField(
-            model_name='circuittermination',
-            name='provider_network',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='circuits.providernetwork'),
-        ),
-        migrations.AddField(
-            model_name='circuittermination',
-            name='site',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='circuittermination',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='circuit',
-            name='provider',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provider'),
-        ),
-        migrations.AddField(
-            model_name='circuit',
-            name='provider_account',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount'),
-        ),
-        migrations.AddField(
-            model_name='circuit',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-    ]

+ 21 - 0
netbox/circuits/migrations/0004_rename_cable_peer.py

@@ -0,0 +1,21 @@
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0003_extend_tag_support'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='circuittermination',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='circuittermination',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+    ]

+ 17 - 0
netbox/circuits/migrations/0032_provider_service_id.py

@@ -0,0 +1,17 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0004_rename_cable_peer'),
+        ('dcim', '0145_site_remove_deprecated_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='providernetwork',
+            name='service_id',
+            field=models.CharField(blank=True, max_length=100),
+        ),
+    ]

+ 44 - 0
netbox/circuits/migrations/0033_standardize_id_fields.py

@@ -0,0 +1,44 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0032_provider_service_id'),
+    ]
+
+    operations = [
+        # Model IDs
+        migrations.AlterField(
+            model_name='circuit',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='circuittermination',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='circuittype',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='provider',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='providernetwork',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+
+        # GFK IDs
+        migrations.AlterField(
+            model_name='circuittermination',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+    ]

+ 38 - 0
netbox/circuits/migrations/0034_created_datetimefield.py

@@ -0,0 +1,38 @@
+# Generated by Django 4.0.2 on 2022-02-08 18:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0033_standardize_id_fields'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='circuit',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='circuittermination',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='circuittype',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='provider',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='providernetwork',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+    ]

+ 19 - 0
netbox/circuits/migrations/0035_provider_asns.py

@@ -0,0 +1,19 @@
+# Generated by Django 4.0.3 on 2022-03-30 20:27
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0057_created_datetimefield'),
+        ('circuits', '0034_created_datetimefield'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='provider',
+            name='asns',
+            field=models.ManyToManyField(blank=True, related_name='providers', to='ipam.asn'),
+        ),
+    ]

+ 28 - 0
netbox/circuits/migrations/0036_circuit_termination_date_tags_custom_fields.py

@@ -0,0 +1,28 @@
+from utilities.json import CustomFieldJSONEncoder
+from django.db import migrations, models
+import taggit.managers
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0035_provider_asns'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='circuit',
+            name='termination_date',
+            field=models.DateField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='custom_field_data',
+            field=models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+    ]

+ 16 - 0
netbox/circuits/migrations/0037_new_cabling_models.py

@@ -0,0 +1,16 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0036_circuit_termination_date_tags_custom_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='circuittermination',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+    ]

+ 20 - 0
netbox/circuits/migrations/0038_cabling_cleanup.py

@@ -0,0 +1,20 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0037_new_cabling_models'),
+        ('dcim', '0160_populate_cable_ends'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='circuittermination',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='circuittermination',
+            name='_link_peer_type',
+        ),
+    ]

+ 39 - 0
netbox/circuits/migrations/0039_unique_constraints.py

@@ -0,0 +1,39 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0038_cabling_cleanup'),
+    ]
+
+    operations = [
+        migrations.RemoveConstraint(
+            model_name='providernetwork',
+            name='circuits_providernetwork_provider_name',
+        ),
+        migrations.AlterUniqueTogether(
+            name='circuit',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='circuittermination',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='providernetwork',
+            unique_together=set(),
+        ),
+        migrations.AddConstraint(
+            model_name='circuit',
+            constraint=models.UniqueConstraint(fields=('provider', 'cid'), name='circuits_circuit_unique_provider_cid'),
+        ),
+        migrations.AddConstraint(
+            model_name='circuittermination',
+            constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'),
+        ),
+        migrations.AddConstraint(
+            model_name='providernetwork',
+            constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'),
+        ),
+    ]

+ 59 - 0
netbox/circuits/migrations/0040_provider_remove_deprecated_fields.py

@@ -0,0 +1,59 @@
+import os
+
+from django.db import migrations
+from django.db.utils import DataError
+
+
+def check_legacy_data(apps, schema_editor):
+    """
+    Abort the migration if any legacy provider fields still contain data.
+    """
+    Provider = apps.get_model('circuits', 'Provider')
+
+    provider_count = Provider.objects.exclude(asn__isnull=True).count()
+    if provider_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
+        raise DataError(
+            f"Unable to proceed with deleting asn field from Provider model: Found {provider_count} "
+            f"providers with legacy ASN data. Please ensure all legacy provider ASN data has been "
+            f"migrated to ASN objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA "
+            f"environment variable to bypass this safeguard and delete all legacy provider ASN data."
+        )
+
+    provider_count = Provider.objects.exclude(admin_contact='', noc_contact='', portal_url='').count()
+    if provider_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
+        raise DataError(
+            f"Unable to proceed with deleting contact fields from Provider model: Found {provider_count} "
+            f"providers with legacy contact data. Please ensure all legacy provider contact data has been "
+            f"migrated to contact objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA "
+            f"environment variable to bypass this safeguard and delete all legacy provider contact data."
+        )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0039_unique_constraints'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=check_legacy_data,
+            reverse_code=migrations.RunPython.noop
+        ),
+        migrations.RemoveField(
+            model_name='provider',
+            name='admin_contact',
+        ),
+        migrations.RemoveField(
+            model_name='provider',
+            name='asn',
+        ),
+        migrations.RemoveField(
+            model_name='provider',
+            name='noc_contact',
+        ),
+        migrations.RemoveField(
+            model_name='provider',
+            name='portal_url',
+        ),
+    ]

+ 18 - 0
netbox/circuits/migrations/0041_standardize_description_comments.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0040_provider_remove_deprecated_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='provider',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+    ]

+ 91 - 0
netbox/circuits/migrations/0042_provideraccount.py

@@ -0,0 +1,91 @@
+from django.db import migrations, models
+import django.db.models.deletion
+import taggit.managers
+import utilities.json
+
+
+def create_provideraccounts_from_providers(apps, schema_editor):
+    """
+    Migrate Account in Provider model to separate account model
+    """
+    Provider = apps.get_model('circuits', 'Provider')
+    ProviderAccount = apps.get_model('circuits', 'ProviderAccount')
+
+    provider_accounts = []
+    for provider in Provider.objects.all():
+        if provider.account:
+            provider_accounts.append(ProviderAccount(
+                provider=provider,
+                account=provider.account
+            ))
+    ProviderAccount.objects.bulk_create(provider_accounts, batch_size=100)
+
+
+def restore_providers_from_provideraccounts(apps, schema_editor):
+    """
+    Restore Provider account values from auto-generated ProviderAccounts
+    """
+    ProviderAccount = apps.get_model('circuits', 'ProviderAccount')
+    provider_accounts = ProviderAccount.objects.order_by('pk')
+    for provideraccount in provider_accounts:
+        if provider_accounts.filter(provider=provideraccount.provider)[0] == provideraccount:
+            provideraccount.provider.account = provideraccount.account
+            provideraccount.provider.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0084_staging'),
+        ('circuits', '0041_standardize_description_comments'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ProviderAccount',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('comments', models.TextField(blank=True)),
+                ('account', models.CharField(max_length=100)),
+                ('name', models.CharField(blank=True, max_length=100)),
+                ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='accounts', to='circuits.provider')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'ordering': ('provider', 'account'),
+            },
+        ),
+        migrations.AddConstraint(
+            model_name='provideraccount',
+            constraint=models.UniqueConstraint(condition=models.Q(('name', ''), _negated=True), fields=('provider', 'name'), name='circuits_provideraccount_unique_provider_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='provideraccount',
+            constraint=models.UniqueConstraint(fields=('provider', 'account'), name='circuits_provideraccount_unique_provider_account'),
+        ),
+        migrations.RunPython(
+            create_provideraccounts_from_providers, restore_providers_from_provideraccounts
+        ),
+        migrations.RemoveField(
+            model_name='provider',
+            name='account',
+        ),
+        migrations.AddField(
+            model_name='circuit',
+            name='provider_account',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount', null=True, blank=True),
+            preserve_default=False,
+        ),
+        migrations.AlterModelOptions(
+            name='circuit',
+            options={'ordering': ['provider', 'provider_account', 'cid']},
+        ),
+        migrations.AddConstraint(
+            model_name='circuit',
+            constraint=models.UniqueConstraint(fields=('provider_account', 'cid'), name='circuits_circuit_unique_provideraccount_cid'),
+        ),
+    ]

+ 2 - 0
netbox/circuits/migrations/0043_circuittype_color.py

@@ -1,3 +1,5 @@
+# Generated by Django 4.2.5 on 2023-10-20 21:25
+
 from django.db import migrations
 import utilities.fields
 

+ 33 - 64
netbox/core/migrations/0001_initial.py

@@ -1,8 +1,9 @@
-from django.conf import settings
+# Generated by Django 4.1.5 on 2023-02-02 02:37
+
 import django.core.validators
 from django.db import migrations, models
 import django.db.models.deletion
-import extras.utils
+import taggit.managers
 import utilities.json
 
 
@@ -11,39 +12,10 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        ('contenttypes', '0002_remove_content_type_name'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('extras', '0084_staging'),
     ]
 
     operations = [
-        migrations.CreateModel(
-            name='AutoSyncRecord',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('object_id', models.PositiveBigIntegerField()),
-            ],
-            options={
-                'verbose_name': 'auto sync record',
-                'verbose_name_plural': 'auto sync records',
-            },
-        ),
-        migrations.CreateModel(
-            name='DataFile',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True)),
-                ('last_updated', models.DateTimeField(editable=False)),
-                ('path', models.CharField(editable=False, max_length=1000)),
-                ('size', models.PositiveIntegerField(editable=False)),
-                ('hash', models.CharField(editable=False, max_length=64, validators=[django.core.validators.RegexValidator(message='Length must be 64 hexadecimal characters.', regex='^[0-9a-f]{64}$')])),
-                ('data', models.BinaryField()),
-            ],
-            options={
-                'verbose_name': 'data file',
-                'verbose_name_plural': 'data files',
-                'ordering': ('source', 'path'),
-            },
-        ),
         migrations.CreateModel(
             name='DataSource',
             fields=[
@@ -61,54 +33,51 @@ class Migration(migrations.Migration):
                 ('ignore_rules', models.TextField(blank=True)),
                 ('parameters', models.JSONField(blank=True, null=True)),
                 ('last_synced', models.DateTimeField(blank=True, editable=False, null=True)),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
             ],
             options={
-                'verbose_name': 'data source',
-                'verbose_name_plural': 'data sources',
                 'ordering': ('name',),
             },
         ),
         migrations.CreateModel(
-            name='ManagedFile',
+            name='DataFile',
             fields=[
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('data_path', models.CharField(blank=True, editable=False, max_length=1000)),
-                ('auto_sync_enabled', models.BooleanField(default=False)),
-                ('data_synced', models.DateTimeField(blank=True, editable=False, null=True)),
                 ('created', models.DateTimeField(auto_now_add=True)),
-                ('last_updated', models.DateTimeField(blank=True, editable=False, null=True)),
-                ('file_root', models.CharField(max_length=1000)),
-                ('file_path', models.FilePathField(editable=False)),
-                ('data_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile')),
-                ('data_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource')),
+                ('last_updated', models.DateTimeField(editable=False)),
+                ('path', models.CharField(editable=False, max_length=1000)),
+                ('size', models.PositiveIntegerField(editable=False)),
+                ('hash', models.CharField(editable=False, max_length=64, validators=[django.core.validators.RegexValidator(message='Length must be 64 hexadecimal characters.', regex='^[0-9a-f]{64}$')])),
+                ('data', models.BinaryField()),
+                ('source', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='datafiles', to='core.datasource')),
             ],
             options={
-                'verbose_name': 'managed file',
-                'verbose_name_plural': 'managed files',
-                'ordering': ('file_root', 'file_path'),
+                'ordering': ('source', 'path'),
             },
         ),
+        migrations.AddConstraint(
+            model_name='datafile',
+            constraint=models.UniqueConstraint(fields=('source', 'path'), name='core_datafile_unique_source_path'),
+        ),
+        migrations.AddIndex(
+            model_name='datafile',
+            index=models.Index(fields=['source', 'path'], name='core_datafile_source_path'),
+        ),
         migrations.CreateModel(
-            name='Job',
+            name='AutoSyncRecord',
             fields=[
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('object_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('name', models.CharField(max_length=200)),
-                ('created', models.DateTimeField(auto_now_add=True)),
-                ('scheduled', models.DateTimeField(blank=True, null=True)),
-                ('interval', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
-                ('started', models.DateTimeField(blank=True, null=True)),
-                ('completed', models.DateTimeField(blank=True, null=True)),
-                ('status', models.CharField(default='pending', max_length=30)),
-                ('data', models.JSONField(blank=True, null=True)),
-                ('job_id', models.UUIDField(unique=True)),
-                ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='contenttypes.contenttype')),
-                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+                ('object_id', models.PositiveBigIntegerField()),
+                ('datafile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.datafile')),
+                ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')),
             ],
-            options={
-                'verbose_name': 'job',
-                'verbose_name_plural': 'jobs',
-                'ordering': ['-created'],
-            },
+        ),
+        migrations.AddIndex(
+            model_name='autosyncrecord',
+            index=models.Index(fields=['object_type', 'object_id'], name='core_autosy_object__c17bac_idx'),
+        ),
+        migrations.AddConstraint(
+            model_name='autosyncrecord',
+            constraint=models.UniqueConstraint(fields=('object_type', 'object_id'), name='core_autosyncrecord_object'),
         ),
     ]

+ 40 - 0
netbox/core/migrations/0002_managedfile.py

@@ -0,0 +1,40 @@
+# Generated by Django 4.1.7 on 2023-03-23 17:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ManagedFile',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('data_path', models.CharField(blank=True, editable=False, max_length=1000)),
+                ('data_synced', models.DateTimeField(blank=True, editable=False, null=True)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('last_updated', models.DateTimeField(blank=True, editable=False, null=True)),
+                ('file_root', models.CharField(max_length=1000)),
+                ('file_path', models.FilePathField(editable=False)),
+                ('data_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile')),
+                ('data_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource')),
+                ('auto_sync_enabled', models.BooleanField(default=False)),
+            ],
+            options={
+                'ordering': ('file_root', 'file_path'),
+            },
+        ),
+        migrations.AddIndex(
+            model_name='managedfile',
+            index=models.Index(fields=['file_root', 'file_path'], name='core_managedfile_root_path'),
+        ),
+        migrations.AddConstraint(
+            model_name='managedfile',
+            constraint=models.UniqueConstraint(fields=('file_root', 'file_path'), name='core_managedfile_unique_root_path'),
+        ),
+    ]

+ 0 - 68
netbox/core/migrations/0002_squashed.py

@@ -1,68 +0,0 @@
-from django.db import migrations, models
-import django.db.models.deletion
-import taggit.managers
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('extras', '0001_initial'),
-        ('contenttypes', '0002_remove_content_type_name'),
-        ('core', '0001_initial'),
-    ]
-
-    replaces = [
-        ('core', '0002_managedfile'),
-        ('core', '0003_job'),
-        ('core', '0004_replicate_jobresults'),
-        ('core', '0005_job_created_auto_now'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='datasource',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='datafile',
-            name='source',
-            field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='datafiles', to='core.datasource'),
-        ),
-        migrations.AddField(
-            model_name='autosyncrecord',
-            name='datafile',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='core.datafile'),
-        ),
-        migrations.AddField(
-            model_name='autosyncrecord',
-            name='object_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'),
-        ),
-        migrations.AddIndex(
-            model_name='managedfile',
-            index=models.Index(fields=['file_root', 'file_path'], name='core_managedfile_root_path'),
-        ),
-        migrations.AddConstraint(
-            model_name='managedfile',
-            constraint=models.UniqueConstraint(fields=('file_root', 'file_path'), name='core_managedfile_unique_root_path'),
-        ),
-        migrations.AddIndex(
-            model_name='datafile',
-            index=models.Index(fields=['source', 'path'], name='core_datafile_source_path'),
-        ),
-        migrations.AddConstraint(
-            model_name='datafile',
-            constraint=models.UniqueConstraint(fields=('source', 'path'), name='core_datafile_unique_source_path'),
-        ),
-        migrations.AddIndex(
-            model_name='autosyncrecord',
-            index=models.Index(fields=['object_type', 'object_id'], name='core_autosy_object__c17bac_idx'),
-        ),
-        migrations.AddConstraint(
-            model_name='autosyncrecord',
-            constraint=models.UniqueConstraint(fields=('object_type', 'object_id'), name='core_autosyncrecord_object'),
-        ),
-    ]

+ 39 - 0
netbox/core/migrations/0003_job.py

@@ -0,0 +1,39 @@
+# Generated by Django 4.1.7 on 2023-03-27 15:02
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('core', '0002_managedfile'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Job',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('object_id', models.PositiveBigIntegerField(blank=True, null=True)),
+                ('name', models.CharField(max_length=200)),
+                ('created', models.DateTimeField()),
+                ('scheduled', models.DateTimeField(blank=True, null=True)),
+                ('interval', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
+                ('started', models.DateTimeField(blank=True, null=True)),
+                ('completed', models.DateTimeField(blank=True, null=True)),
+                ('status', models.CharField(default='pending', max_length=30)),
+                ('data', models.JSONField(blank=True, null=True)),
+                ('job_id', models.UUIDField(unique=True)),
+                ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='contenttypes.contenttype')),
+                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['-created'],
+            },
+        ),
+    ]

+ 46 - 0
netbox/core/migrations/0004_replicate_jobresults.py

@@ -0,0 +1,46 @@
+from django.db import migrations
+
+
+def replicate_jobresults(apps, schema_editor):
+    """
+    Replicate existing JobResults to the new Jobs table before deleting the old JobResults table.
+    """
+    Job = apps.get_model('core', 'Job')
+    JobResult = apps.get_model('extras', 'JobResult')
+
+    jobs = []
+    for job_result in JobResult.objects.order_by('pk').iterator(chunk_size=100):
+        jobs.append(
+            Job(
+                object_type=job_result.obj_type,
+                name=job_result.name,
+                created=job_result.created,
+                scheduled=job_result.scheduled,
+                interval=job_result.interval,
+                started=job_result.started,
+                completed=job_result.completed,
+                user=job_result.user,
+                status=job_result.status,
+                data=job_result.data,
+                job_id=job_result.job_id,
+            )
+        )
+        if len(jobs) == 100:
+            Job.objects.bulk_create(jobs)
+            jobs = []
+    if jobs:
+        Job.objects.bulk_create(jobs)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0003_job'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=replicate_jobresults,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 18 - 0
netbox/core/migrations/0005_job_created_auto_now.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.7 on 2023-03-27 17:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0004_replicate_jobresults'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='job',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True),
+        ),
+    ]

+ 2 - 0
netbox/core/migrations/0006_datasource_type_remove_choices.py

@@ -1,3 +1,5 @@
+# Generated by Django 4.2.6 on 2023-10-20 17:47
+
 from django.db import migrations, models
 
 

+ 0 - 979
netbox/dcim/migrations/0001_initial.py

@@ -1,979 +0,0 @@
-# Generated by Django 4.2.9 on 2024-01-18 18:27
-
-import dcim.fields
-import django.contrib.postgres.fields
-import django.core.validators
-from django.db import migrations, models
-import django.db.models.deletion
-import timezone_field.fields
-import utilities.fields
-import utilities.json
-import utilities.ordering
-import utilities.query_functions
-import utilities.tracking
-import utilities.validators
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Cable',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('status', models.CharField(default='connected', max_length=50)),
-                ('label', models.CharField(blank=True, max_length=100)),
-                ('color', utilities.fields.ColorField(blank=True, max_length=6)),
-                ('length', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
-                ('length_unit', models.CharField(blank=True, max_length=50)),
-                ('_abs_length', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
-            ],
-            options={
-                'verbose_name': 'cable',
-                'verbose_name_plural': 'cables',
-                'ordering': ('pk',),
-            },
-        ),
-        migrations.CreateModel(
-            name='CablePath',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('path', models.JSONField(default=list)),
-                ('is_active', models.BooleanField(default=False)),
-                ('is_complete', models.BooleanField(default=False)),
-                ('is_split', models.BooleanField(default=False)),
-                ('_nodes', dcim.fields.PathField(base_field=models.CharField(max_length=40), size=None)),
-            ],
-            options={
-                'verbose_name': 'cable path',
-                'verbose_name_plural': 'cable paths',
-            },
-        ),
-        migrations.CreateModel(
-            name='CableTermination',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('cable_end', models.CharField(max_length=1)),
-                ('termination_id', models.PositiveBigIntegerField()),
-            ],
-            options={
-                'verbose_name': 'cable termination',
-                'verbose_name_plural': 'cable terminations',
-                'ordering': ('cable', 'cable_end', 'pk'),
-            },
-        ),
-        migrations.CreateModel(
-            name='ConsolePort',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('speed', models.PositiveIntegerField(blank=True, null=True)),
-            ],
-            options={
-                'verbose_name': 'console port',
-                'verbose_name_plural': 'console ports',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='ConsolePortTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('type', models.CharField(blank=True, max_length=50)),
-            ],
-            options={
-                'verbose_name': 'console port template',
-                'verbose_name_plural': 'console port templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='ConsoleServerPort',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('speed', models.PositiveIntegerField(blank=True, null=True)),
-            ],
-            options={
-                'verbose_name': 'console server port',
-                'verbose_name_plural': 'console server ports',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='ConsoleServerPortTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('type', models.CharField(blank=True, max_length=50)),
-            ],
-            options={
-                'verbose_name': 'console server port template',
-                'verbose_name_plural': 'console server port templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='Device',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('local_context_data', models.JSONField(blank=True, null=True)),
-                ('name', models.CharField(blank=True, max_length=64, null=True)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize, null=True)),
-                ('serial', models.CharField(blank=True, max_length=50)),
-                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
-                ('position', models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100.5)])),
-                ('face', models.CharField(blank=True, max_length=50)),
-                ('status', models.CharField(default='active', max_length=50)),
-                ('airflow', models.CharField(blank=True, max_length=50)),
-                ('vc_position', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)])),
-                ('vc_priority', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)])),
-                ('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=8, null=True)),
-                ('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
-                ('console_port_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.ConsolePort')),
-                ('console_server_port_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.ConsoleServerPort')),
-                ('power_port_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.PowerPort')),
-                ('power_outlet_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.PowerOutlet')),
-                ('interface_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.Interface')),
-                ('front_port_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.FrontPort')),
-                ('rear_port_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.RearPort')),
-                ('device_bay_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.DeviceBay')),
-                ('module_bay_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.ModuleBay')),
-                ('inventory_item_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device', to_model='dcim.InventoryItem')),
-            ],
-            options={
-                'verbose_name': 'device',
-                'verbose_name_plural': 'devices',
-                'ordering': ('_name', 'pk'),
-            },
-            bases=(utilities.tracking.TrackingModelMixin, models.Model),
-        ),
-        migrations.CreateModel(
-            name='DeviceBay',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-            ],
-            options={
-                'verbose_name': 'device bay',
-                'verbose_name_plural': 'device bays',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='DeviceBayTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-            ],
-            options={
-                'verbose_name': 'device bay template',
-                'verbose_name_plural': 'device bay templates',
-                'ordering': ('device_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='DeviceRole',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
-                ('vm_role', models.BooleanField(default=True)),
-            ],
-            options={
-                'verbose_name': 'device role',
-                'verbose_name_plural': 'device roles',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='DeviceType',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
-                ('weight_unit', models.CharField(blank=True, max_length=50)),
-                ('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('model', models.CharField(max_length=100)),
-                ('slug', models.SlugField(max_length=100)),
-                ('part_number', models.CharField(blank=True, max_length=50)),
-                ('u_height', models.DecimalField(decimal_places=1, default=1.0, max_digits=4)),
-                ('is_full_depth', models.BooleanField(default=True)),
-                ('subdevice_role', models.CharField(blank=True, max_length=50)),
-                ('airflow', models.CharField(blank=True, max_length=50)),
-                ('front_image', models.ImageField(blank=True, upload_to='devicetype-images')),
-                ('rear_image', models.ImageField(blank=True, upload_to='devicetype-images')),
-                ('console_port_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.ConsolePortTemplate')),
-                ('console_server_port_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.ConsoleServerPortTemplate')),
-                ('power_port_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.PowerPortTemplate')),
-                ('power_outlet_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.PowerOutletTemplate')),
-                ('interface_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.InterfaceTemplate')),
-                ('front_port_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.FrontPortTemplate')),
-                ('rear_port_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.RearPortTemplate')),
-                ('device_bay_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.DeviceBayTemplate')),
-                ('module_bay_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.ModuleBayTemplate')),
-                ('inventory_item_template_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='device_type', to_model='dcim.InventoryItemTemplate')),
-            ],
-            options={
-                'verbose_name': 'device type',
-                'verbose_name_plural': 'device types',
-                'ordering': ['manufacturer', 'model'],
-            },
-        ),
-        migrations.CreateModel(
-            name='FrontPort',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('type', models.CharField(max_length=50)),
-                ('color', utilities.fields.ColorField(blank=True, max_length=6)),
-                ('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
-            ],
-            options={
-                'verbose_name': 'front port',
-                'verbose_name_plural': 'front ports',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='FrontPortTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('type', models.CharField(max_length=50)),
-                ('color', utilities.fields.ColorField(blank=True, max_length=6)),
-                ('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
-            ],
-            options={
-                'verbose_name': 'front port template',
-                'verbose_name_plural': 'front port templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='Interface',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('enabled', models.BooleanField(default=True)),
-                ('mac_address', dcim.fields.MACAddressField(blank=True, null=True)),
-                ('mtu', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65536)])),
-                ('mode', models.CharField(blank=True, max_length=50)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)),
-                ('type', models.CharField(max_length=50)),
-                ('mgmt_only', models.BooleanField(default=False)),
-                ('speed', models.PositiveIntegerField(blank=True, null=True)),
-                ('duplex', models.CharField(blank=True, max_length=50, null=True)),
-                ('wwn', dcim.fields.WWNField(blank=True, null=True)),
-                ('rf_role', models.CharField(blank=True, max_length=30)),
-                ('rf_channel', models.CharField(blank=True, max_length=50)),
-                ('rf_channel_frequency', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)),
-                ('rf_channel_width', models.DecimalField(blank=True, decimal_places=3, max_digits=7, null=True)),
-                ('tx_power', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(127)])),
-                ('poe_mode', models.CharField(blank=True, max_length=50)),
-                ('poe_type', models.CharField(blank=True, max_length=50)),
-            ],
-            options={
-                'verbose_name': 'interface',
-                'verbose_name_plural': 'interfaces',
-                'ordering': ('device', utilities.query_functions.CollateAsChar('_name')),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='InterfaceTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)),
-                ('type', models.CharField(max_length=50)),
-                ('enabled', models.BooleanField(default=True)),
-                ('mgmt_only', models.BooleanField(default=False)),
-                ('poe_mode', models.CharField(blank=True, max_length=50)),
-                ('poe_type', models.CharField(blank=True, max_length=50)),
-                ('rf_role', models.CharField(blank=True, max_length=30)),
-            ],
-            options={
-                'verbose_name': 'interface template',
-                'verbose_name_plural': 'interface templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='InventoryItem',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('component_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('part_id', models.CharField(blank=True, max_length=50)),
-                ('serial', models.CharField(blank=True, max_length=50)),
-                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
-                ('discovered', models.BooleanField(default=False)),
-                ('lft', models.PositiveIntegerField(editable=False)),
-                ('rght', models.PositiveIntegerField(editable=False)),
-                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
-                ('level', models.PositiveIntegerField(editable=False)),
-            ],
-            options={
-                'verbose_name': 'inventory item',
-                'verbose_name_plural': 'inventory items',
-                'ordering': ('device__id', 'parent__id', '_name'),
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='InventoryItemRole',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
-            ],
-            options={
-                'verbose_name': 'inventory item role',
-                'verbose_name_plural': 'inventory item roles',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='InventoryItemTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('component_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('part_id', models.CharField(blank=True, max_length=50)),
-                ('lft', models.PositiveIntegerField(editable=False)),
-                ('rght', models.PositiveIntegerField(editable=False)),
-                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
-                ('level', models.PositiveIntegerField(editable=False)),
-            ],
-            options={
-                'verbose_name': 'inventory item template',
-                'verbose_name_plural': 'inventory item templates',
-                'ordering': ('device_type__id', 'parent__id', '_name'),
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='Location',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100)),
-                ('slug', models.SlugField(max_length=100)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('status', models.CharField(default='active', max_length=50)),
-                ('lft', models.PositiveIntegerField(editable=False)),
-                ('rght', models.PositiveIntegerField(editable=False)),
-                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
-                ('level', models.PositiveIntegerField(editable=False)),
-            ],
-            options={
-                'verbose_name': 'location',
-                'verbose_name_plural': 'locations',
-                'ordering': ['site', 'name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='Manufacturer',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-            ],
-            options={
-                'verbose_name': 'manufacturer',
-                'verbose_name_plural': 'manufacturers',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='Module',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('local_context_data', models.JSONField(blank=True, null=True)),
-                ('status', models.CharField(default='active', max_length=50)),
-                ('serial', models.CharField(blank=True, max_length=50)),
-                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
-            ],
-            options={
-                'verbose_name': 'module',
-                'verbose_name_plural': 'modules',
-                'ordering': ('module_bay',),
-            },
-        ),
-        migrations.CreateModel(
-            name='ModuleBay',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('position', models.CharField(blank=True, max_length=30)),
-            ],
-            options={
-                'verbose_name': 'module bay',
-                'verbose_name_plural': 'module bays',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='ModuleBayTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('position', models.CharField(blank=True, max_length=30)),
-            ],
-            options={
-                'verbose_name': 'module bay template',
-                'verbose_name_plural': 'module bay templates',
-                'ordering': ('device_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='ModuleType',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
-                ('weight_unit', models.CharField(blank=True, max_length=50)),
-                ('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('model', models.CharField(max_length=100)),
-                ('part_number', models.CharField(blank=True, max_length=50)),
-            ],
-            options={
-                'verbose_name': 'module type',
-                'verbose_name_plural': 'module types',
-                'ordering': ('manufacturer', 'model'),
-            },
-        ),
-        migrations.CreateModel(
-            name='Platform',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-            ],
-            options={
-                'verbose_name': 'platform',
-                'verbose_name_plural': 'platforms',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='PowerFeed',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('name', models.CharField(max_length=100)),
-                ('status', models.CharField(default='active', max_length=50)),
-                ('type', models.CharField(default='primary', max_length=50)),
-                ('supply', models.CharField(default='ac', max_length=50)),
-                ('phase', models.CharField(default='single-phase', max_length=50)),
-                ('voltage', models.SmallIntegerField(validators=[utilities.validators.ExclusionValidator([0])])),
-                ('amperage', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1)])),
-                ('max_utilization', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
-                ('available_power', models.PositiveIntegerField(default=0, editable=False)),
-            ],
-            options={
-                'verbose_name': 'power feed',
-                'verbose_name_plural': 'power feeds',
-                'ordering': ['power_panel', 'name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='PowerOutlet',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('feed_leg', models.CharField(blank=True, max_length=50)),
-            ],
-            options={
-                'verbose_name': 'power outlet',
-                'verbose_name_plural': 'power outlets',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='PowerOutletTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('feed_leg', models.CharField(blank=True, max_length=50)),
-            ],
-            options={
-                'verbose_name': 'power outlet template',
-                'verbose_name_plural': 'power outlet templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='PowerPanel',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('name', models.CharField(max_length=100)),
-            ],
-            options={
-                'verbose_name': 'power panel',
-                'verbose_name_plural': 'power panels',
-                'ordering': ['site', 'name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='PowerPort',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('maximum_draw', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
-                ('allocated_draw', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
-            ],
-            options={
-                'verbose_name': 'power port',
-                'verbose_name_plural': 'power ports',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='PowerPortTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('maximum_draw', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
-                ('allocated_draw', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
-            ],
-            options={
-                'verbose_name': 'power port template',
-                'verbose_name_plural': 'power port templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='Rack',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('weight', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True)),
-                ('weight_unit', models.CharField(blank=True, max_length=50)),
-                ('_abs_weight', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('name', models.CharField(max_length=100)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('facility_id', models.CharField(blank=True, max_length=50, null=True)),
-                ('status', models.CharField(default='active', max_length=50)),
-                ('serial', models.CharField(blank=True, max_length=50)),
-                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
-                ('type', models.CharField(blank=True, max_length=50)),
-                ('width', models.PositiveSmallIntegerField(default=19)),
-                ('u_height', models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
-                ('starting_unit', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)])),
-                ('desc_units', models.BooleanField(default=False)),
-                ('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)),
-                ('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)),
-                ('outer_unit', models.CharField(blank=True, max_length=50)),
-                ('max_weight', models.PositiveIntegerField(blank=True, null=True)),
-                ('_abs_max_weight', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('mounting_depth', models.PositiveSmallIntegerField(blank=True, null=True)),
-            ],
-            options={
-                'verbose_name': 'rack',
-                'verbose_name_plural': 'racks',
-                'ordering': ('site', 'location', '_name', 'pk'),
-            },
-        ),
-        migrations.CreateModel(
-            name='RackReservation',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('comments', models.TextField(blank=True)),
-                ('units', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(), size=None)),
-                ('description', models.CharField(max_length=200)),
-            ],
-            options={
-                'verbose_name': 'rack reservation',
-                'verbose_name_plural': 'rack reservations',
-                'ordering': ['created', 'pk'],
-            },
-        ),
-        migrations.CreateModel(
-            name='RackRole',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
-            ],
-            options={
-                'verbose_name': 'rack role',
-                'verbose_name_plural': 'rack roles',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='RearPort',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('cable_end', models.CharField(blank=True, max_length=1)),
-                ('mark_connected', models.BooleanField(default=False)),
-                ('type', models.CharField(max_length=50)),
-                ('color', utilities.fields.ColorField(blank=True, max_length=6)),
-                ('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
-            ],
-            options={
-                'verbose_name': 'rear port',
-                'verbose_name_plural': 'rear ports',
-                'ordering': ('device', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='RearPortTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=64)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('label', models.CharField(blank=True, max_length=64)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('type', models.CharField(max_length=50)),
-                ('color', utilities.fields.ColorField(blank=True, max_length=6)),
-                ('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
-            ],
-            options={
-                'verbose_name': 'rear port template',
-                'verbose_name_plural': 'rear port templates',
-                'ordering': ('device_type', 'module_type', '_name'),
-                'abstract': False,
-            },
-            bases=(models.Model, utilities.tracking.TrackingModelMixin),
-        ),
-        migrations.CreateModel(
-            name='Region',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100)),
-                ('slug', models.SlugField(max_length=100)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('lft', models.PositiveIntegerField(editable=False)),
-                ('rght', models.PositiveIntegerField(editable=False)),
-                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
-                ('level', models.PositiveIntegerField(editable=False)),
-            ],
-            options={
-                'verbose_name': 'region',
-                'verbose_name_plural': 'regions',
-            },
-        ),
-        migrations.CreateModel(
-            name='Site',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('status', models.CharField(default='active', max_length=50)),
-                ('facility', models.CharField(blank=True, max_length=50)),
-                ('time_zone', timezone_field.fields.TimeZoneField(blank=True)),
-                ('physical_address', models.CharField(blank=True, max_length=200)),
-                ('shipping_address', models.CharField(blank=True, max_length=200)),
-                ('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=8, null=True)),
-                ('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
-            ],
-            options={
-                'verbose_name': 'site',
-                'verbose_name_plural': 'sites',
-                'ordering': ('_name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='SiteGroup',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=100)),
-                ('slug', models.SlugField(max_length=100)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('lft', models.PositiveIntegerField(editable=False)),
-                ('rght', models.PositiveIntegerField(editable=False)),
-                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
-                ('level', models.PositiveIntegerField(editable=False)),
-            ],
-            options={
-                'verbose_name': 'site group',
-                'verbose_name_plural': 'site groups',
-            },
-        ),
-        migrations.CreateModel(
-            name='VirtualChassis',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('comments', models.TextField(blank=True)),
-                ('name', models.CharField(max_length=64)),
-                ('domain', models.CharField(blank=True, max_length=30)),
-                ('member_count', utilities.fields.CounterCacheField(default=0, editable=False, to_field='virtual_chassis', to_model='dcim.Device')),
-            ],
-            options={
-                'verbose_name': 'virtual chassis',
-                'verbose_name_plural': 'virtual chassis',
-                'ordering': ['name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='VirtualDeviceContext',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('name', models.CharField(max_length=64)),
-                ('status', models.CharField(max_length=50)),
-                ('identifier', models.PositiveSmallIntegerField(blank=True, null=True)),
-                ('comments', models.TextField(blank=True)),
-                ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vdcs', to='dcim.device')),
-            ],
-            options={
-                'verbose_name': 'virtual device context',
-                'verbose_name_plural': 'virtual device contexts',
-                'ordering': ['name'],
-            },
-        ),
-    ]

+ 664 - 0
netbox/dcim/migrations/0001_squashed.py

@@ -0,0 +1,664 @@
+import dcim.fields
+import ipam.fields
+import django.contrib.postgres.fields
+from utilities.json import CustomFieldJSONEncoder
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import timezone_field.fields
+import utilities.fields
+import utilities.ordering
+import utilities.query_functions
+import utilities.validators
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    replaces = [
+        ('dcim', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Cable',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('termination_a_id', models.PositiveIntegerField()),
+                ('termination_b_id', models.PositiveIntegerField()),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('status', models.CharField(default='connected', max_length=50)),
+                ('label', models.CharField(blank=True, max_length=100)),
+                ('color', utilities.fields.ColorField(blank=True, max_length=6)),
+                ('length', models.PositiveSmallIntegerField(blank=True, null=True)),
+                ('length_unit', models.CharField(blank=True, max_length=50)),
+                ('_abs_length', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
+            ],
+            options={
+                'ordering': ['pk'],
+            },
+        ),
+        migrations.CreateModel(
+            name='CablePath',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('origin_id', models.PositiveIntegerField()),
+                ('destination_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('path', dcim.fields.PathField(base_field=models.CharField(max_length=40), size=None)),
+                ('is_active', models.BooleanField(default=False)),
+                ('is_split', models.BooleanField(default=False)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ConsolePort',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('speed', models.PositiveSmallIntegerField(blank=True, null=True)),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='ConsolePortTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('type', models.CharField(blank=True, max_length=50)),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='ConsoleServerPort',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('speed', models.PositiveSmallIntegerField(blank=True, null=True)),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='ConsoleServerPortTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('type', models.CharField(blank=True, max_length=50)),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Device',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('local_context_data', models.JSONField(blank=True, null=True)),
+                ('name', models.CharField(blank=True, max_length=64, null=True)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize, null=True)),
+                ('serial', models.CharField(blank=True, max_length=50)),
+                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
+                ('position', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
+                ('face', models.CharField(blank=True, max_length=50)),
+                ('status', models.CharField(default='active', max_length=50)),
+                ('vc_position', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)])),
+                ('vc_priority', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)])),
+                ('comments', models.TextField(blank=True)),
+            ],
+            options={
+                'ordering': ('_name', 'pk'),
+            },
+        ),
+        migrations.CreateModel(
+            name='DeviceBay',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='DeviceBayTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='DeviceRole',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
+                ('vm_role', models.BooleanField(default=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.CreateModel(
+            name='DeviceType',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('model', models.CharField(max_length=100)),
+                ('slug', models.SlugField(max_length=100)),
+                ('part_number', models.CharField(blank=True, max_length=50)),
+                ('u_height', models.PositiveSmallIntegerField(default=1)),
+                ('is_full_depth', models.BooleanField(default=True)),
+                ('subdevice_role', models.CharField(blank=True, max_length=50)),
+                ('front_image', models.ImageField(blank=True, upload_to='devicetype-images')),
+                ('rear_image', models.ImageField(blank=True, upload_to='devicetype-images')),
+                ('comments', models.TextField(blank=True)),
+            ],
+            options={
+                'ordering': ['manufacturer', 'model'],
+            },
+        ),
+        migrations.CreateModel(
+            name='FrontPort',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('type', models.CharField(max_length=50)),
+                ('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='FrontPortTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('type', models.CharField(max_length=50)),
+                ('rear_port_position', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Interface',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('enabled', models.BooleanField(default=True)),
+                ('mac_address', dcim.fields.MACAddressField(blank=True, null=True)),
+                ('mtu', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65536)])),
+                ('mode', models.CharField(blank=True, max_length=50)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)),
+                ('type', models.CharField(max_length=50)),
+                ('mgmt_only', models.BooleanField(default=False)),
+            ],
+            options={
+                'ordering': ('device', utilities.query_functions.CollateAsChar('_name')),
+            },
+        ),
+        migrations.CreateModel(
+            name='InterfaceTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize_interface)),
+                ('type', models.CharField(max_length=50)),
+                ('mgmt_only', models.BooleanField(default=False)),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='InventoryItem',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('part_id', models.CharField(blank=True, max_length=50)),
+                ('serial', models.CharField(blank=True, max_length=50)),
+                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
+                ('discovered', models.BooleanField(default=False)),
+                ('lft', models.PositiveIntegerField(editable=False)),
+                ('rght', models.PositiveIntegerField(editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(editable=False)),
+            ],
+            options={
+                'ordering': ('device__id', 'parent__id', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Location',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100)),
+                ('slug', models.SlugField(max_length=100)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('lft', models.PositiveIntegerField(editable=False)),
+                ('rght', models.PositiveIntegerField(editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(editable=False)),
+            ],
+            options={
+                'ordering': ['site', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Manufacturer',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.CreateModel(
+            name='Platform',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('napalm_driver', models.CharField(blank=True, max_length=50)),
+                ('napalm_args', models.JSONField(blank=True, null=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.CreateModel(
+            name='PowerFeed',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('name', models.CharField(max_length=100)),
+                ('status', models.CharField(default='active', max_length=50)),
+                ('type', models.CharField(default='primary', max_length=50)),
+                ('supply', models.CharField(default='ac', max_length=50)),
+                ('phase', models.CharField(default='single-phase', max_length=50)),
+                ('voltage', models.SmallIntegerField(validators=[utilities.validators.ExclusionValidator([0])])),
+                ('amperage', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1)])),
+                ('max_utilization', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
+                ('available_power', models.PositiveIntegerField(default=0, editable=False)),
+                ('comments', models.TextField(blank=True)),
+            ],
+            options={
+                'ordering': ['power_panel', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='PowerOutlet',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('feed_leg', models.CharField(blank=True, max_length=50)),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='PowerOutletTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('feed_leg', models.CharField(blank=True, max_length=50)),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='PowerPanel',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100)),
+            ],
+            options={
+                'ordering': ['site', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='PowerPort',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('maximum_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
+                ('allocated_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='PowerPortTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('maximum_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
+                ('allocated_draw', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)])),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Rack',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('facility_id', models.CharField(blank=True, max_length=50, null=True)),
+                ('status', models.CharField(default='active', max_length=50)),
+                ('serial', models.CharField(blank=True, max_length=50)),
+                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
+                ('type', models.CharField(blank=True, max_length=50)),
+                ('width', models.PositiveSmallIntegerField(default=19)),
+                ('u_height', models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
+                ('desc_units', models.BooleanField(default=False)),
+                ('outer_width', models.PositiveSmallIntegerField(blank=True, null=True)),
+                ('outer_depth', models.PositiveSmallIntegerField(blank=True, null=True)),
+                ('outer_unit', models.CharField(blank=True, max_length=50)),
+                ('comments', models.TextField(blank=True)),
+            ],
+            options={
+                'ordering': ('site', 'location', '_name', 'pk'),
+            },
+        ),
+        migrations.CreateModel(
+            name='RackReservation',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('units', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(), size=None)),
+                ('description', models.CharField(max_length=200)),
+            ],
+            options={
+                'ordering': ['created', 'pk'],
+            },
+        ),
+        migrations.CreateModel(
+            name='RackRole',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.CreateModel(
+            name='RearPort',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('_cable_peer_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('mark_connected', models.BooleanField(default=False)),
+                ('type', models.CharField(max_length=50)),
+                ('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='RearPortTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('type', models.CharField(max_length=50)),
+                ('positions', models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(1024)])),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Region',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('lft', models.PositiveIntegerField(editable=False)),
+                ('rght', models.PositiveIntegerField(editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(editable=False)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Site',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('status', models.CharField(default='active', max_length=50)),
+                ('facility', models.CharField(blank=True, max_length=50)),
+                ('asn', ipam.fields.ASNField(blank=True, null=True)),
+                ('time_zone', timezone_field.fields.TimeZoneField(blank=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('physical_address', models.CharField(blank=True, max_length=200)),
+                ('shipping_address', models.CharField(blank=True, max_length=200)),
+                ('latitude', models.DecimalField(blank=True, decimal_places=6, max_digits=8, null=True)),
+                ('longitude', models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True)),
+                ('contact_name', models.CharField(blank=True, max_length=50)),
+                ('contact_phone', models.CharField(blank=True, max_length=20)),
+                ('contact_email', models.EmailField(blank=True, max_length=254)),
+                ('comments', models.TextField(blank=True)),
+            ],
+            options={
+                'ordering': ('_name',),
+            },
+        ),
+        migrations.CreateModel(
+            name='SiteGroup',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('lft', models.PositiveIntegerField(editable=False)),
+                ('rght', models.PositiveIntegerField(editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(editable=False)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='VirtualChassis',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('domain', models.CharField(blank=True, max_length=30)),
+                ('master', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.device')),
+            ],
+            options={
+                'verbose_name_plural': 'virtual chassis',
+                'ordering': ['name'],
+            },
+        ),
+    ]

+ 288 - 11
netbox/dcim/migrations/0002_squashed.py

@@ -1,16 +1,18 @@
+from django.conf import settings
 from django.db import migrations, models
 import django.db.models.deletion
+import mptt.fields
 import taggit.managers
 
 
 class Migration(migrations.Migration):
 
-    initial = True
-
     dependencies = [
-        ('extras', '0001_initial'),
-        ('ipam', '0001_initial'),
         ('dcim', '0001_initial'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('extras', '0001_initial'),
+        ('tenancy', '0001_initial'),
     ]
 
     replaces = [
@@ -19,18 +21,293 @@ class Migration(migrations.Migration):
 
     operations = [
         migrations.AddField(
-            model_name='virtualdevicecontext',
-            name='primary_ip4',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
+            model_name='virtualchassis',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='sitegroup',
+            name='parent',
+            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.sitegroup'),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='group',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.sitegroup'),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='region',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.region'),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sites', to='tenancy.tenant'),
+        ),
+        migrations.AddField(
+            model_name='region',
+            name='parent',
+            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.region'),
+        ),
+        migrations.AddField(
+            model_name='rearporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='rackreservation',
+            name='rack',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='dcim.rack'),
         ),
         migrations.AddField(
-            model_name='virtualdevicecontext',
-            name='primary_ip6',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
+            model_name='rackreservation',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
         ),
         migrations.AddField(
-            model_name='virtualdevicecontext',
+            model_name='rackreservation',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='rackreservations', to='tenancy.tenant'),
+        ),
+        migrations.AddField(
+            model_name='rackreservation',
+            name='user',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='location',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='racks', to='dcim.location'),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='role',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.rackrole'),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='site',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.site'),
+        ),
+        migrations.AddField(
+            model_name='rack',
             name='tags',
             field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
         ),
+        migrations.AddField(
+            model_name='rack',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='tenancy.tenant'),
+        ),
+        migrations.AddField(
+            model_name='powerporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='_path',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='location',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.location'),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='site',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dcim.site'),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlettemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlettemplate',
+            name='power_port',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlet_templates', to='dcim.powerporttemplate'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='_path',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='power_port',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlets', to='dcim.powerport'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='_path',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='power_panel',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.powerpanel'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='rack',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.rack'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='platform',
+            name='manufacturer',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='dcim.manufacturer'),
+        ),
+        migrations.AddField(
+            model_name='location',
+            name='parent',
+            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.location'),
+        ),
+        migrations.AddField(
+            model_name='location',
+            name='site',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='dcim.site'),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='manufacturer',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_items', to='dcim.manufacturer'),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='parent',
+            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.inventoryitem'),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='_path',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='lag',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_interfaces', to='dcim.interface'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='parent',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_interfaces', to='dcim.interface'),
+        ),
     ]

+ 0 - 1190
netbox/dcim/migrations/0003_squashed.py

@@ -1,1190 +0,0 @@
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.db.models.functions.text
-import mptt.fields
-import taggit.managers
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('extras', '0001_initial'),
-        ('virtualization', '0001_initial'),
-        ('contenttypes', '0002_remove_content_type_name'),
-        ('dcim', '0002_squashed'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('ipam', '0001_initial'),
-        ('wireless', '0001_initial'),
-        ('tenancy', '0001_initial'),
-    ]
-
-    replaces = [
-        ('dcim', '0003_auto_20160628_1721'),
-        ('dcim', '0004_auto_20160701_2049'),
-        ('dcim', '0005_auto_20160706_1722'),
-        ('dcim', '0006_add_device_primary_ip4_ip6'),
-        ('dcim', '0007_device_copy_primary_ip'),
-        ('dcim', '0008_device_remove_primary_ip'),
-        ('dcim', '0009_site_32bit_asn_support'),
-        ('dcim', '0010_devicebay_installed_device_set_null'),
-        ('dcim', '0011_devicetype_part_number'),
-        ('dcim', '0012_site_rack_device_add_tenant'),
-        ('dcim', '0013_add_interface_form_factors'),
-        ('dcim', '0014_rack_add_type_width'),
-        ('dcim', '0015_rack_add_u_height_validator'),
-        ('dcim', '0016_module_add_manufacturer'),
-        ('dcim', '0017_rack_add_role'),
-        ('dcim', '0018_device_add_asset_tag'),
-        ('dcim', '0019_new_iface_form_factors'),
-        ('dcim', '0020_rack_desc_units'),
-        ('dcim', '0021_add_ff_flexstack'),
-        ('dcim', '0022_color_names_to_rgb'),
-        ('dcim', '0023_devicetype_comments'),
-        ('dcim', '0024_site_add_contact_fields'),
-        ('dcim', '0025_devicetype_add_interface_ordering'),
-        ('dcim', '0026_add_rack_reservations'),
-        ('dcim', '0027_device_add_site'),
-        ('dcim', '0028_device_copy_rack_to_site'),
-        ('dcim', '0029_allow_rackless_devices'),
-        ('dcim', '0030_interface_add_lag'),
-        ('dcim', '0031_regions'),
-        ('dcim', '0032_device_increase_name_length'),
-        ('dcim', '0033_rackreservation_rack_editable'),
-        ('dcim', '0034_rename_module_to_inventoryitem'),
-        ('dcim', '0035_device_expand_status_choices'),
-        ('dcim', '0036_add_ff_juniper_vcp'),
-        ('dcim', '0037_unicode_literals'),
-        ('dcim', '0038_wireless_interfaces'),
-        ('dcim', '0039_interface_add_enabled_mtu'),
-        ('dcim', '0040_inventoryitem_add_asset_tag_description'),
-        ('dcim', '0041_napalm_integration'),
-        ('dcim', '0042_interface_ff_10ge_cx4'),
-        ('dcim', '0043_device_component_name_lengths'),
-        ('dcim', '0044_virtualization'),
-        ('dcim', '0045_devicerole_vm_role'),
-        ('dcim', '0046_rack_lengthen_facility_id'),
-        ('dcim', '0047_more_100ge_form_factors'),
-        ('dcim', '0048_rack_serial'),
-        ('dcim', '0049_rackreservation_change_user'),
-        ('dcim', '0050_interface_vlan_tagging'),
-        ('dcim', '0051_rackreservation_tenant'),
-        ('dcim', '0052_virtual_chassis'),
-        ('dcim', '0053_platform_manufacturer'),
-        ('dcim', '0054_site_status_timezone_description'),
-        ('dcim', '0055_virtualchassis_ordering'),
-        ('dcim', '0056_django2'),
-        ('dcim', '0057_tags'),
-        ('dcim', '0058_relax_rack_naming_constraints'),
-        ('dcim', '0059_site_latitude_longitude'),
-        ('dcim', '0060_change_logging'),
-        ('dcim', '0061_platform_napalm_args'),
-        ('dcim', '0062_interface_mtu'),
-        ('dcim', '0063_device_local_context_data'),
-        ('dcim', '0064_remove_platform_rpc_client'),
-        ('dcim', '0065_front_rear_ports'),
-        ('dcim', '0066_cables'),
-        ('dcim', '0067_device_type_remove_qualifiers'),
-        ('dcim', '0068_rack_new_fields'),
-        ('dcim', '0069_deprecate_nullablecharfield'),
-        ('dcim', '0070_custom_tag_models'),
-        ('dcim', '0071_device_components_add_description'),
-        ('dcim', '0072_powerfeeds'),
-        ('dcim', '0073_interface_form_factor_to_type'),
-        ('dcim', '0074_increase_field_length_platform_name_slug'),
-        ('dcim', '0075_cable_devices'),
-        ('dcim', '0076_console_port_types'),
-        ('dcim', '0077_power_types'),
-        ('dcim', '0078_3569_site_fields'),
-        ('dcim', '0079_3569_rack_fields'),
-        ('dcim', '0080_3569_devicetype_fields'),
-        ('dcim', '0081_3569_device_fields'),
-        ('dcim', '0082_3569_interface_fields'),
-        ('dcim', '0082_3569_port_fields'),
-        ('dcim', '0083_3569_cable_fields'),
-        ('dcim', '0084_3569_powerfeed_fields'),
-        ('dcim', '0085_3569_poweroutlet_fields'),
-        ('dcim', '0086_device_name_nonunique'),
-        ('dcim', '0087_role_descriptions'),
-        ('dcim', '0088_powerfeed_available_power'),
-        ('dcim', '0089_deterministic_ordering'),
-        ('dcim', '0090_cable_termination_models'),
-        ('dcim', '0091_interface_type_other'),
-        ('dcim', '0092_fix_rack_outer_unit'),
-        ('dcim', '0093_device_component_ordering'),
-        ('dcim', '0094_device_component_template_ordering'),
-        ('dcim', '0095_primary_model_ordering'),
-        ('dcim', '0096_interface_ordering'),
-        ('dcim', '0097_interfacetemplate_type_other'),
-        ('dcim', '0098_devicetype_images'),
-        ('dcim', '0099_powerfeed_negative_voltage'),
-        ('dcim', '0100_mptt_remove_indexes'),
-        ('dcim', '0101_nested_rackgroups'),
-        ('dcim', '0102_nested_rackgroups_rebuild'),
-        ('dcim', '0103_standardize_description'),
-        ('dcim', '0104_correct_infiniband_types'),
-        ('dcim', '0105_interface_name_collation'),
-        ('dcim', '0106_role_default_color'),
-        ('dcim', '0107_component_labels'),
-        ('dcim', '0108_add_tags'),
-        ('dcim', '0109_interface_remove_vm'),
-        ('dcim', '0110_virtualchassis_name'),
-        ('dcim', '0111_component_template_description'),
-        ('dcim', '0112_standardize_components'),
-        ('dcim', '0113_nullbooleanfield_to_booleanfield'),
-        ('dcim', '0114_update_jsonfield'),
-        ('dcim', '0115_rackreservation_order'),
-        ('dcim', '0116_rearport_max_positions'),
-        ('dcim', '0117_custom_field_data'),
-        ('dcim', '0118_inventoryitem_mptt'),
-        ('dcim', '0119_inventoryitem_mptt_rebuild'),
-        ('dcim', '0120_cache_cable_peer'),
-        ('dcim', '0121_cablepath'),
-        ('dcim', '0122_standardize_name_length'),
-        ('dcim', '0123_standardize_models'),
-        ('dcim', '0124_mark_connected'),
-        ('dcim', '0125_console_port_speed'),
-        ('dcim', '0126_rename_rackgroup_location'),
-        ('dcim', '0127_device_location'),
-        ('dcim', '0128_device_location_populate'),
-        ('dcim', '0129_interface_parent'),
-        ('dcim', '0130_sitegroup'),
-        ('dcim', '0131_consoleport_speed'),
-        ('dcim', '0132_cable_length'),
-        ('dcim', '0133_port_colors'),
-        ('dcim', '0134_interface_wwn_bridge'),
-        ('dcim', '0135_tenancy_extensions'),
-        ('dcim', '0136_device_airflow'),
-        ('dcim', '0137_relax_uniqueness_constraints'),
-        ('dcim', '0138_extend_tag_support'),
-        ('dcim', '0139_rename_cable_peer'),
-        ('dcim', '0140_wireless'),
-        ('dcim', '0141_asn_model'),
-        ('dcim', '0142_rename_128gfc_qsfp28'),
-        ('dcim', '0143_remove_primary_for_related_name'),
-        ('dcim', '0144_fix_cable_abs_length'),
-        ('dcim', '0145_site_remove_deprecated_fields'),
-        ('dcim', '0146_modules'),
-        ('dcim', '0147_inventoryitemrole'),
-        ('dcim', '0148_inventoryitem_component'),
-        ('dcim', '0149_inventoryitem_templates'),
-        ('dcim', '0150_interface_vrf'),
-        ('dcim', '0151_interface_speed_duplex'),
-        ('dcim', '0152_standardize_id_fields'),
-        ('dcim', '0153_created_datetimefield'),
-        ('dcim', '0154_half_height_rack_units'),
-        ('dcim', '0155_interface_poe_mode_type'),
-        ('dcim', '0156_location_status'),
-        ('dcim', '0157_new_cabling_models'),
-        ('dcim', '0158_populate_cable_terminations'),
-        ('dcim', '0159_populate_cable_paths'),
-        ('dcim', '0160_populate_cable_ends'),
-        ('dcim', '0161_cabling_cleanup'),
-        ('dcim', '0162_unique_constraints'),
-        ('dcim', '0163_weight_fields'),
-        ('dcim', '0164_rack_mounting_depth'),
-        ('dcim', '0165_standardize_description_comments'),
-        ('dcim', '0166_virtualdevicecontext'),
-        ('dcim', '0167_module_status'),
-        ('dcim', '0168_interface_template_enabled'),
-        ('dcim', '0169_devicetype_default_platform'),
-        ('dcim', '0170_configtemplate'),
-        ('dcim', '0171_cabletermination_change_logging'),
-        ('dcim', '0172_larger_power_draw_values'),
-        ('dcim', '0173_remove_napalm_fields'),
-        ('dcim', '0174_device_latitude_device_longitude'),
-        ('dcim', '0174_rack_starting_unit'),  # Duplicate number
-        ('dcim', '0175_device_oob_ip'),
-        ('dcim', '0176_device_component_counters'),
-        ('dcim', '0177_devicetype_component_counters'),
-        ('dcim', '0178_virtual_chassis_member_counter'),
-        ('dcim', '0179_interfacetemplate_rf_role'),
-        ('dcim', '0180_powerfeed_tenant'),
-        ('dcim', '0181_rename_device_role_device_role'),
-        ('dcim', '0182_zero_length_cable_fix'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='virtualdevicecontext',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vdcs', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='virtualchassis',
-            name='master',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='virtualchassis',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='sitegroup',
-            name='parent',
-            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.sitegroup'),
-        ),
-        migrations.AddField(
-            model_name='sitegroup',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='site',
-            name='asns',
-            field=models.ManyToManyField(blank=True, related_name='sites', to='ipam.asn'),
-        ),
-        migrations.AddField(
-            model_name='site',
-            name='group',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.sitegroup'),
-        ),
-        migrations.AddField(
-            model_name='site',
-            name='region',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.region'),
-        ),
-        migrations.AddField(
-            model_name='site',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='site',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sites', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='region',
-            name='parent',
-            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.region'),
-        ),
-        migrations.AddField(
-            model_name='region',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='rearporttemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='rearporttemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='rearport',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='rearport',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='rearport',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='rearport',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='rackrole',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='rackreservation',
-            name='rack',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='dcim.rack'),
-        ),
-        migrations.AddField(
-            model_name='rackreservation',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='rackreservation',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='rackreservations', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='rackreservation',
-            name='user',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
-        ),
-        migrations.AddField(
-            model_name='rack',
-            name='location',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='racks', to='dcim.location'),
-        ),
-        migrations.AddField(
-            model_name='rack',
-            name='role',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.rackrole'),
-        ),
-        migrations.AddField(
-            model_name='rack',
-            name='site',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='rack',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='rack',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='racks', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='powerporttemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='powerporttemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='powerport',
-            name='_path',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
-        ),
-        migrations.AddField(
-            model_name='powerport',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='powerport',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='powerport',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='powerport',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='powerpanel',
-            name='location',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.location'),
-        ),
-        migrations.AddField(
-            model_name='powerpanel',
-            name='site',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='powerpanel',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlettemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlettemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlettemplate',
-            name='power_port',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlet_templates', to='dcim.powerporttemplate'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlet',
-            name='_path',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlet',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlet',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlet',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlet',
-            name='power_port',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlets', to='dcim.powerport'),
-        ),
-        migrations.AddField(
-            model_name='poweroutlet',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='powerfeed',
-            name='_path',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
-        ),
-        migrations.AddField(
-            model_name='powerfeed',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='powerfeed',
-            name='power_panel',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.powerpanel'),
-        ),
-        migrations.AddField(
-            model_name='powerfeed',
-            name='rack',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.rack'),
-        ),
-        migrations.AddField(
-            model_name='powerfeed',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='powerfeed',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='power_feeds', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='platform',
-            name='config_template',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='extras.configtemplate'),
-        ),
-        migrations.AddField(
-            model_name='platform',
-            name='manufacturer',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='dcim.manufacturer'),
-        ),
-        migrations.AddField(
-            model_name='platform',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='moduletype',
-            name='manufacturer',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='module_types', to='dcim.manufacturer'),
-        ),
-        migrations.AddField(
-            model_name='moduletype',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='modulebaytemplate',
-            name='device_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='modulebay',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='modulebay',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='module',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modules', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='module',
-            name='module_bay',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='installed_module', to='dcim.modulebay'),
-        ),
-        migrations.AddField(
-            model_name='module',
-            name='module_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='module',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='manufacturer',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='location',
-            name='parent',
-            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.location'),
-        ),
-        migrations.AddField(
-            model_name='location',
-            name='site',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='location',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='location',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='locations', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitemtemplate',
-            name='component_type',
-            field=models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ('consoleporttemplate', 'consoleserverporttemplate', 'frontporttemplate', 'interfacetemplate', 'poweroutlettemplate', 'powerporttemplate', 'rearporttemplate'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitemtemplate',
-            name='device_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitemtemplate',
-            name='manufacturer',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_templates', to='dcim.manufacturer'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitemtemplate',
-            name='parent',
-            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.inventoryitemtemplate'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitemtemplate',
-            name='role',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_templates', to='dcim.inventoryitemrole'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitemrole',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitem',
-            name='component_type',
-            field=models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'poweroutlet', 'powerport', 'rearport'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitem',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitem',
-            name='manufacturer',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_items', to='dcim.manufacturer'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitem',
-            name='parent',
-            field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.inventoryitem'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitem',
-            name='role',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_items', to='dcim.inventoryitemrole'),
-        ),
-        migrations.AddField(
-            model_name='inventoryitem',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='interfacetemplate',
-            name='bridge',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interfacetemplate'),
-        ),
-        migrations.AddField(
-            model_name='interfacetemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='interfacetemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='_path',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='bridge',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interface'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='lag',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_interfaces', to='dcim.interface'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='parent',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_interfaces', to='dcim.interface'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='tagged_vlans',
-            field=models.ManyToManyField(blank=True, related_name='interfaces_as_tagged', to='ipam.vlan'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='untagged_vlan',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces_as_untagged', to='ipam.vlan'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='vdcs',
-            field=models.ManyToManyField(related_name='interfaces', to='dcim.virtualdevicecontext'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='vrf',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces', to='ipam.vrf'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='wireless_lans',
-            field=models.ManyToManyField(blank=True, related_name='interfaces', to='wireless.wirelesslan'),
-        ),
-        migrations.AddField(
-            model_name='interface',
-            name='wireless_link',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wireless.wirelesslink'),
-        ),
-        migrations.AddField(
-            model_name='frontporttemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='frontporttemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='frontporttemplate',
-            name='rear_port',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontport_templates', to='dcim.rearporttemplate'),
-        ),
-        migrations.AddField(
-            model_name='frontport',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='frontport',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='frontport',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='frontport',
-            name='rear_port',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontports', to='dcim.rearport'),
-        ),
-        migrations.AddField(
-            model_name='frontport',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='devicetype',
-            name='default_platform',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.platform'),
-        ),
-        migrations.AddField(
-            model_name='devicetype',
-            name='manufacturer',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='device_types', to='dcim.manufacturer'),
-        ),
-        migrations.AddField(
-            model_name='devicetype',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='devicerole',
-            name='config_template',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='device_roles', to='extras.configtemplate'),
-        ),
-        migrations.AddField(
-            model_name='devicerole',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='devicebaytemplate',
-            name='device_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='devicebay',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='devicebay',
-            name='installed_device',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parent_bay', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='devicebay',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='cluster',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='virtualization.cluster'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='config_template',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(class)ss', to='extras.configtemplate'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='device_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='location',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.location'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='oob_ip',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='platform',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='dcim.platform'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='primary_ip4',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='primary_ip6',
-            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='rack',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.rack'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='role',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.devicerole'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='site',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='device',
-            name='virtual_chassis',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='dcim.virtualchassis'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverporttemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverporttemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverport',
-            name='_path',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverport',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverport',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverport',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='consoleserverport',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='consoleporttemplate',
-            name='device_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='consoleporttemplate',
-            name='module_type',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
-        ),
-        migrations.AddField(
-            model_name='consoleport',
-            name='_path',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
-        ),
-        migrations.AddField(
-            model_name='consoleport',
-            name='cable',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='consoleport',
-            name='device',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='consoleport',
-            name='module',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
-        ),
-        migrations.AddField(
-            model_name='consoleport',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='cabletermination',
-            name='_device',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.device'),
-        ),
-        migrations.AddField(
-            model_name='cabletermination',
-            name='_location',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.location'),
-        ),
-        migrations.AddField(
-            model_name='cabletermination',
-            name='_rack',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.rack'),
-        ),
-        migrations.AddField(
-            model_name='cabletermination',
-            name='_site',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='cabletermination',
-            name='cable',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='dcim.cable'),
-        ),
-        migrations.AddField(
-            model_name='cabletermination',
-            name='termination_type',
-            field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'),
-        ),
-        migrations.AddField(
-            model_name='cable',
-            name='tags',
-            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
-        ),
-        migrations.AddField(
-            model_name='cable',
-            name='tenant',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cables', to='tenancy.tenant'),
-        ),
-        migrations.AddConstraint(
-            model_name='virtualdevicecontext',
-            constraint=models.UniqueConstraint(fields=('device', 'identifier'), name='dcim_virtualdevicecontext_device_identifier'),
-        ),
-        migrations.AddConstraint(
-            model_name='virtualdevicecontext',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_virtualdevicecontext_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='sitegroup',
-            constraint=models.UniqueConstraint(fields=('parent', 'name'), name='dcim_sitegroup_parent_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='sitegroup',
-            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_sitegroup_name', violation_error_message='A top-level site group with this name already exists.'),
-        ),
-        migrations.AddConstraint(
-            model_name='sitegroup',
-            constraint=models.UniqueConstraint(fields=('parent', 'slug'), name='dcim_sitegroup_parent_slug'),
-        ),
-        migrations.AddConstraint(
-            model_name='sitegroup',
-            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('slug',), name='dcim_sitegroup_slug', violation_error_message='A top-level site group with this slug already exists.'),
-        ),
-        migrations.AddConstraint(
-            model_name='region',
-            constraint=models.UniqueConstraint(fields=('parent', 'name'), name='dcim_region_parent_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='region',
-            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_region_name', violation_error_message='A top-level region with this name already exists.'),
-        ),
-        migrations.AddConstraint(
-            model_name='region',
-            constraint=models.UniqueConstraint(fields=('parent', 'slug'), name='dcim_region_parent_slug'),
-        ),
-        migrations.AddConstraint(
-            model_name='region',
-            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('slug',), name='dcim_region_slug', violation_error_message='A top-level region with this slug already exists.'),
-        ),
-        migrations.AddConstraint(
-            model_name='rearporttemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_rearporttemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='rearporttemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_rearporttemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='rearport',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_rearport_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='rack',
-            constraint=models.UniqueConstraint(fields=('location', 'name'), name='dcim_rack_unique_location_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='rack',
-            constraint=models.UniqueConstraint(fields=('location', 'facility_id'), name='dcim_rack_unique_location_facility_id'),
-        ),
-        migrations.AddConstraint(
-            model_name='powerporttemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_powerporttemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='powerporttemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_powerporttemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='powerport',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_powerport_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='powerpanel',
-            constraint=models.UniqueConstraint(fields=('site', 'name'), name='dcim_powerpanel_unique_site_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='poweroutlettemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_poweroutlettemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='poweroutlettemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_poweroutlettemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='poweroutlet',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_poweroutlet_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='powerfeed',
-            constraint=models.UniqueConstraint(fields=('power_panel', 'name'), name='dcim_powerfeed_unique_power_panel_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='moduletype',
-            constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_moduletype_unique_manufacturer_model'),
-        ),
-        migrations.AddConstraint(
-            model_name='modulebaytemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_modulebaytemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='modulebay',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_modulebay_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='location',
-            constraint=models.UniqueConstraint(fields=('site', 'parent', 'name'), name='dcim_location_parent_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='location',
-            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'name'), name='dcim_location_name', violation_error_message='A location with this name already exists within the specified site.'),
-        ),
-        migrations.AddConstraint(
-            model_name='location',
-            constraint=models.UniqueConstraint(fields=('site', 'parent', 'slug'), name='dcim_location_parent_slug'),
-        ),
-        migrations.AddConstraint(
-            model_name='location',
-            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'slug'), name='dcim_location_slug', violation_error_message='A location with this slug already exists within the specified site.'),
-        ),
-        migrations.AddConstraint(
-            model_name='inventoryitemtemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'parent', 'name'), name='dcim_inventoryitemtemplate_unique_device_type_parent_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='inventoryitem',
-            constraint=models.UniqueConstraint(fields=('device', 'parent', 'name'), name='dcim_inventoryitem_unique_device_parent_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='interfacetemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_interfacetemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='interfacetemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_interfacetemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='interface',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_interface_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='frontporttemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_frontporttemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='frontporttemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_frontporttemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='frontporttemplate',
-            constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontporttemplate_unique_rear_port_position'),
-        ),
-        migrations.AddConstraint(
-            model_name='frontport',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_frontport_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='frontport',
-            constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontport_unique_rear_port_position'),
-        ),
-        migrations.AddConstraint(
-            model_name='devicetype',
-            constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_devicetype_unique_manufacturer_model'),
-        ),
-        migrations.AddConstraint(
-            model_name='devicetype',
-            constraint=models.UniqueConstraint(fields=('manufacturer', 'slug'), name='dcim_devicetype_unique_manufacturer_slug'),
-        ),
-        migrations.AddConstraint(
-            model_name='devicebaytemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_devicebaytemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='devicebay',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_devicebay_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='device',
-            constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('site'), models.F('tenant'), name='dcim_device_unique_name_site_tenant'),
-        ),
-        migrations.AddConstraint(
-            model_name='device',
-            constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('site'), condition=models.Q(('tenant__isnull', True)), name='dcim_device_unique_name_site', violation_error_message='Device name must be unique per site.'),
-        ),
-        migrations.AddConstraint(
-            model_name='device',
-            constraint=models.UniqueConstraint(fields=('rack', 'position', 'face'), name='dcim_device_unique_rack_position_face'),
-        ),
-        migrations.AddConstraint(
-            model_name='device',
-            constraint=models.UniqueConstraint(fields=('virtual_chassis', 'vc_position'), name='dcim_device_unique_virtual_chassis_vc_position'),
-        ),
-        migrations.AddConstraint(
-            model_name='consoleserverporttemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleserverporttemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='consoleserverporttemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleserverporttemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='consoleserverport',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleserverport_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='consoleporttemplate',
-            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleporttemplate_unique_device_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='consoleporttemplate',
-            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleporttemplate_unique_module_type_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='consoleport',
-            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleport_unique_device_name'),
-        ),
-        migrations.AddConstraint(
-            model_name='cabletermination',
-            constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='dcim_cabletermination_unique_termination'),
-        ),
-    ]

+ 485 - 0
netbox/dcim/migrations/0003_squashed_0130.py

@@ -0,0 +1,485 @@
+from django.db import migrations, models
+import django.db.models.deletion
+import taggit.managers
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0002_auto_20160622_1821'),
+        ('virtualization', '0001_virtualization'),
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('ipam', '0001_initial'),
+        ('tenancy', '0001_initial'),
+        ('extras', '0002_custom_fields'),
+    ]
+
+    replaces = [
+        ('dcim', '0003_auto_20160628_1721'),
+        ('dcim', '0004_auto_20160701_2049'),
+        ('dcim', '0005_auto_20160706_1722'),
+        ('dcim', '0006_add_device_primary_ip4_ip6'),
+        ('dcim', '0007_device_copy_primary_ip'),
+        ('dcim', '0008_device_remove_primary_ip'),
+        ('dcim', '0009_site_32bit_asn_support'),
+        ('dcim', '0010_devicebay_installed_device_set_null'),
+        ('dcim', '0011_devicetype_part_number'),
+        ('dcim', '0012_site_rack_device_add_tenant'),
+        ('dcim', '0013_add_interface_form_factors'),
+        ('dcim', '0014_rack_add_type_width'),
+        ('dcim', '0015_rack_add_u_height_validator'),
+        ('dcim', '0016_module_add_manufacturer'),
+        ('dcim', '0017_rack_add_role'),
+        ('dcim', '0018_device_add_asset_tag'),
+        ('dcim', '0019_new_iface_form_factors'),
+        ('dcim', '0020_rack_desc_units'),
+        ('dcim', '0021_add_ff_flexstack'),
+        ('dcim', '0022_color_names_to_rgb'),
+        ('dcim', '0023_devicetype_comments'),
+        ('dcim', '0024_site_add_contact_fields'),
+        ('dcim', '0025_devicetype_add_interface_ordering'),
+        ('dcim', '0026_add_rack_reservations'),
+        ('dcim', '0027_device_add_site'),
+        ('dcim', '0028_device_copy_rack_to_site'),
+        ('dcim', '0029_allow_rackless_devices'),
+        ('dcim', '0030_interface_add_lag'),
+        ('dcim', '0031_regions'),
+        ('dcim', '0032_device_increase_name_length'),
+        ('dcim', '0033_rackreservation_rack_editable'),
+        ('dcim', '0034_rename_module_to_inventoryitem'),
+        ('dcim', '0035_device_expand_status_choices'),
+        ('dcim', '0036_add_ff_juniper_vcp'),
+        ('dcim', '0037_unicode_literals'),
+        ('dcim', '0038_wireless_interfaces'),
+        ('dcim', '0039_interface_add_enabled_mtu'),
+        ('dcim', '0040_inventoryitem_add_asset_tag_description'),
+        ('dcim', '0041_napalm_integration'),
+        ('dcim', '0042_interface_ff_10ge_cx4'),
+        ('dcim', '0043_device_component_name_lengths'),
+        ('dcim', '0044_virtualization'),
+        ('dcim', '0045_devicerole_vm_role'),
+        ('dcim', '0046_rack_lengthen_facility_id'),
+        ('dcim', '0047_more_100ge_form_factors'),
+        ('dcim', '0048_rack_serial'),
+        ('dcim', '0049_rackreservation_change_user'),
+        ('dcim', '0050_interface_vlan_tagging'),
+        ('dcim', '0051_rackreservation_tenant'),
+        ('dcim', '0052_virtual_chassis'),
+        ('dcim', '0053_platform_manufacturer'),
+        ('dcim', '0054_site_status_timezone_description'),
+        ('dcim', '0055_virtualchassis_ordering'),
+        ('dcim', '0056_django2'),
+        ('dcim', '0057_tags'),
+        ('dcim', '0058_relax_rack_naming_constraints'),
+        ('dcim', '0059_site_latitude_longitude'),
+        ('dcim', '0060_change_logging'),
+        ('dcim', '0061_platform_napalm_args'),
+        ('dcim', '0062_interface_mtu'),
+        ('dcim', '0063_device_local_context_data'),
+        ('dcim', '0064_remove_platform_rpc_client'),
+        ('dcim', '0065_front_rear_ports'),
+        ('dcim', '0066_cables'),
+        ('dcim', '0067_device_type_remove_qualifiers'),
+        ('dcim', '0068_rack_new_fields'),
+        ('dcim', '0069_deprecate_nullablecharfield'),
+        ('dcim', '0070_custom_tag_models'),
+        ('dcim', '0071_device_components_add_description'),
+        ('dcim', '0072_powerfeeds'),
+        ('dcim', '0073_interface_form_factor_to_type'),
+        ('dcim', '0074_increase_field_length_platform_name_slug'),
+        ('dcim', '0075_cable_devices'),
+        ('dcim', '0076_console_port_types'),
+        ('dcim', '0077_power_types'),
+        ('dcim', '0078_3569_site_fields'),
+        ('dcim', '0079_3569_rack_fields'),
+        ('dcim', '0080_3569_devicetype_fields'),
+        ('dcim', '0081_3569_device_fields'),
+        ('dcim', '0082_3569_interface_fields'),
+        ('dcim', '0082_3569_port_fields'),
+        ('dcim', '0083_3569_cable_fields'),
+        ('dcim', '0084_3569_powerfeed_fields'),
+        ('dcim', '0085_3569_poweroutlet_fields'),
+        ('dcim', '0086_device_name_nonunique'),
+        ('dcim', '0087_role_descriptions'),
+        ('dcim', '0088_powerfeed_available_power'),
+        ('dcim', '0089_deterministic_ordering'),
+        ('dcim', '0090_cable_termination_models'),
+        ('dcim', '0091_interface_type_other'),
+        ('dcim', '0092_fix_rack_outer_unit'),
+        ('dcim', '0093_device_component_ordering'),
+        ('dcim', '0094_device_component_template_ordering'),
+        ('dcim', '0095_primary_model_ordering'),
+        ('dcim', '0096_interface_ordering'),
+        ('dcim', '0097_interfacetemplate_type_other'),
+        ('dcim', '0098_devicetype_images'),
+        ('dcim', '0099_powerfeed_negative_voltage'),
+        ('dcim', '0100_mptt_remove_indexes'),
+        ('dcim', '0101_nested_rackgroups'),
+        ('dcim', '0102_nested_rackgroups_rebuild'),
+        ('dcim', '0103_standardize_description'),
+        ('dcim', '0104_correct_infiniband_types'),
+        ('dcim', '0105_interface_name_collation'),
+        ('dcim', '0106_role_default_color'),
+        ('dcim', '0107_component_labels'),
+        ('dcim', '0108_add_tags'),
+        ('dcim', '0109_interface_remove_vm'),
+        ('dcim', '0110_virtualchassis_name'),
+        ('dcim', '0111_component_template_description'),
+        ('dcim', '0112_standardize_components'),
+        ('dcim', '0113_nullbooleanfield_to_booleanfield'),
+        ('dcim', '0114_update_jsonfield'),
+        ('dcim', '0115_rackreservation_order'),
+        ('dcim', '0116_rearport_max_positions'),
+        ('dcim', '0117_custom_field_data'),
+        ('dcim', '0118_inventoryitem_mptt'),
+        ('dcim', '0119_inventoryitem_mptt_rebuild'),
+        ('dcim', '0120_cache_cable_peer'),
+        ('dcim', '0121_cablepath'),
+        ('dcim', '0122_standardize_name_length'),
+        ('dcim', '0123_standardize_models'),
+        ('dcim', '0124_mark_connected'),
+        ('dcim', '0125_console_port_speed'),
+        ('dcim', '0126_rename_rackgroup_location'),
+        ('dcim', '0127_device_location'),
+        ('dcim', '0128_device_location_populate'),
+        ('dcim', '0129_interface_parent'),
+        ('dcim', '0130_sitegroup'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='tagged_vlans',
+            field=models.ManyToManyField(blank=True, related_name='interfaces_as_tagged', to='ipam.VLAN'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='untagged_vlan',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces_as_untagged', to='ipam.vlan'),
+        ),
+        migrations.AddField(
+            model_name='frontporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='frontporttemplate',
+            name='rear_port',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontport_templates', to='dcim.rearporttemplate'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='rear_port',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frontports', to='dcim.rearport'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='manufacturer',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='device_types', to='dcim.manufacturer'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='devicebaytemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='devicebay',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='devicebay',
+            name='installed_device',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parent_bay', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='devicebay',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='cluster',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='virtualization.cluster'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='device_role',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.devicerole'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='location',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.location'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='platform',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='devices', to='dcim.platform'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='primary_ip4',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip4_for', to='ipam.ipaddress'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='primary_ip6',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='primary_ip6_for', to='ipam.ipaddress'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='rack',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.rack'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='site',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.site'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='tenancy.tenant'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='virtual_chassis',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='dcim.virtualchassis'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='_path',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='consoleporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='_path',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dcim.cablepath'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='cable',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.cable'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='cablepath',
+            name='destination_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='cablepath',
+            name='origin_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='_termination_a_device',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='_termination_b_device',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device'),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='termination_a_type',
+            field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='termination_b_type',
+            field=models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='rearporttemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='rearport',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='rack',
+            unique_together={('location', 'facility_id'), ('location', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerporttemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerport',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerpanel',
+            unique_together={('site', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlettemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlet',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerfeed',
+            unique_together={('power_panel', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='location',
+            unique_together={('site', 'name'), ('site', 'slug')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='inventoryitem',
+            unique_together={('device', 'parent', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='interfacetemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='interface',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='frontporttemplate',
+            unique_together={('rear_port', 'rear_port_position'), ('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='frontport',
+            unique_together={('device', 'name'), ('rear_port', 'rear_port_position')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicetype',
+            unique_together={('manufacturer', 'model'), ('manufacturer', 'slug')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicebaytemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicebay',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='device',
+            unique_together={('rack', 'position', 'face'), ('virtual_chassis', 'vc_position'), ('site', 'tenant', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverporttemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverport',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleporttemplate',
+            unique_together={('device_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleport',
+            unique_together={('device', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='cablepath',
+            unique_together={('origin_type', 'origin_id')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='cable',
+            unique_together={('termination_b_type', 'termination_b_id'), ('termination_a_type', 'termination_a_id')},
+        ),
+    ]

+ 21 - 0
netbox/dcim/migrations/0131_consoleport_speed.py

@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0130_sitegroup'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='consoleport',
+            name='speed',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverport',
+            name='speed',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+    ]

+ 16 - 0
netbox/dcim/migrations/0132_cable_length.py

@@ -0,0 +1,16 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0131_consoleport_speed'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='cable',
+            name='length',
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
+        ),
+    ]

+ 32 - 0
netbox/dcim/migrations/0133_port_colors.py

@@ -0,0 +1,32 @@
+from django.db import migrations
+import utilities.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0132_cable_length'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='frontport',
+            name='color',
+            field=utilities.fields.ColorField(blank=True, max_length=6),
+        ),
+        migrations.AddField(
+            model_name='frontporttemplate',
+            name='color',
+            field=utilities.fields.ColorField(blank=True, max_length=6),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='color',
+            field=utilities.fields.ColorField(blank=True, max_length=6),
+        ),
+        migrations.AddField(
+            model_name='rearporttemplate',
+            name='color',
+            field=utilities.fields.ColorField(blank=True, max_length=6),
+        ),
+    ]

+ 23 - 0
netbox/dcim/migrations/0134_interface_wwn_bridge.py

@@ -0,0 +1,23 @@
+import dcim.fields
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0133_port_colors'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='wwn',
+            field=dcim.fields.WWNField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='bridge',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interface'),
+        ),
+    ]

+ 23 - 0
netbox/dcim/migrations/0135_tenancy_extensions.py

@@ -0,0 +1,23 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tenancy', '0002_tenant_ordering'),
+        ('dcim', '0134_interface_wwn_bridge'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='location',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='locations', to='tenancy.tenant'),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cables', to='tenancy.tenant'),
+        ),
+    ]

+ 21 - 0
netbox/dcim/migrations/0136_device_airflow.py

@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0135_tenancy_extensions'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='devicetype',
+            name='airflow',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='airflow',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+    ]

+ 83 - 0
netbox/dcim/migrations/0137_relax_uniqueness_constraints.py

@@ -0,0 +1,83 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0136_device_airflow'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='region',
+            name='name',
+            field=models.CharField(max_length=100),
+        ),
+        migrations.AlterField(
+            model_name='region',
+            name='slug',
+            field=models.SlugField(max_length=100),
+        ),
+        migrations.AlterField(
+            model_name='sitegroup',
+            name='name',
+            field=models.CharField(max_length=100),
+        ),
+        migrations.AlterField(
+            model_name='sitegroup',
+            name='slug',
+            field=models.SlugField(max_length=100),
+        ),
+        migrations.AlterUniqueTogether(
+            name='location',
+            unique_together=set(),
+        ),
+        migrations.AddConstraint(
+            model_name='location',
+            constraint=models.UniqueConstraint(fields=('site', 'parent', 'name'), name='dcim_location_parent_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='location',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('site', 'name'), name='dcim_location_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='location',
+            constraint=models.UniqueConstraint(fields=('site', 'parent', 'slug'), name='dcim_location_parent_slug'),
+        ),
+        migrations.AddConstraint(
+            model_name='location',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('site', 'slug'), name='dcim_location_slug'),
+        ),
+        migrations.AddConstraint(
+            model_name='region',
+            constraint=models.UniqueConstraint(fields=('parent', 'name'), name='dcim_region_parent_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='region',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('name',), name='dcim_region_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='region',
+            constraint=models.UniqueConstraint(fields=('parent', 'slug'), name='dcim_region_parent_slug'),
+        ),
+        migrations.AddConstraint(
+            model_name='region',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('slug',), name='dcim_region_slug'),
+        ),
+        migrations.AddConstraint(
+            model_name='sitegroup',
+            constraint=models.UniqueConstraint(fields=('parent', 'name'), name='dcim_sitegroup_parent_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='sitegroup',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('name',), name='dcim_sitegroup_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='sitegroup',
+            constraint=models.UniqueConstraint(fields=('parent', 'slug'), name='dcim_sitegroup_parent_slug'),
+        ),
+        migrations.AddConstraint(
+            model_name='sitegroup',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent', None)), fields=('slug',), name='dcim_sitegroup_slug'),
+        ),
+    ]

+ 50 - 0
netbox/dcim/migrations/0138_extend_tag_support.py

@@ -0,0 +1,50 @@
+# Generated by Django 3.2.8 on 2021-10-21 14:50
+
+from django.db import migrations
+import taggit.managers
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0062_clear_secrets_changelog'),
+        ('dcim', '0137_relax_uniqueness_constraints'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='devicerole',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='location',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='manufacturer',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='platform',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='rackrole',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='region',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='sitegroup',
+            name='tags',
+            field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
+        ),
+    ]

+ 91 - 0
netbox/dcim/migrations/0139_rename_cable_peer.py

@@ -0,0 +1,91 @@
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0138_extend_tag_support'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='consoleport',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='consoleport',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='consoleserverport',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='consoleserverport',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='frontport',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='frontport',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='interface',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='interface',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='powerfeed',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='powerfeed',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='poweroutlet',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='poweroutlet',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='powerport',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='powerport',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+        migrations.RenameField(
+            model_name='rearport',
+            old_name='_cable_peer_id',
+            new_name='_link_peer_id',
+        ),
+        migrations.RenameField(
+            model_name='rearport',
+            old_name='_cable_peer_type',
+            new_name='_link_peer_type',
+        ),
+    ]

+ 49 - 0
netbox/dcim/migrations/0140_wireless.py

@@ -0,0 +1,49 @@
+from django.db import migrations, models
+import django.core.validators
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0139_rename_cable_peer'),
+        ('wireless', '0001_wireless'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='rf_role',
+            field=models.CharField(blank=True, max_length=30),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='rf_channel',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='rf_channel_frequency',
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='rf_channel_width',
+            field=models.DecimalField(blank=True, decimal_places=3, max_digits=7, null=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='tx_power',
+            field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(127)]),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='wireless_lans',
+            field=models.ManyToManyField(blank=True, related_name='interfaces', to='wireless.WirelessLAN'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='wireless_link',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wireless.wirelesslink'),
+        ),
+    ]

+ 19 - 0
netbox/dcim/migrations/0141_asn_model.py

@@ -0,0 +1,19 @@
+# Generated by Django 3.2.8 on 2021-11-02 16:16
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0053_asn_model'),
+        ('dcim', '0140_wireless'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='site',
+            name='asns',
+            field=models.ManyToManyField(blank=True, related_name='sites', to='ipam.ASN'),
+        ),
+    ]

+ 29 - 0
netbox/dcim/migrations/0142_rename_128gfc_qsfp28.py

@@ -0,0 +1,29 @@
+from django.db import migrations
+
+OLD_VALUE = '128gfc-sfp28'
+NEW_VALUE = '128gfc-qsfp28'
+
+
+def correct_type(apps, schema_editor):
+    """
+    Correct TYPE_128GFC_QSFP28 interface type.
+    """
+    Interface = apps.get_model('dcim', 'Interface')
+    InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate')
+
+    for model in (Interface, InterfaceTemplate):
+        model.objects.filter(type=OLD_VALUE).update(type=NEW_VALUE)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0141_asn_model'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=correct_type,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 23 - 0
netbox/dcim/migrations/0143_remove_primary_for_related_name.py

@@ -0,0 +1,23 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0053_asn_model'),
+        ('dcim', '0142_rename_128gfc_qsfp28'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='device',
+            name='primary_ip4',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='primary_ip6',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress'),
+        ),
+    ]

+ 31 - 0
netbox/dcim/migrations/0144_fix_cable_abs_length.py

@@ -0,0 +1,31 @@
+from django.db import migrations
+
+from utilities.utils import to_meters
+
+
+def recalculate_abs_length(apps, schema_editor):
+    """
+    Recalculate absolute lengths for all cables with a length and length unit defined. Fixes
+    incorrectly calculated values as reported under bug #8377.
+    """
+    Cable = apps.get_model('dcim', 'Cable')
+
+    cables = Cable.objects.filter(length__isnull=False).exclude(length_unit='')
+    for cable in cables:
+        cable._abs_length = to_meters(cable.length, cable.length_unit)
+
+    Cable.objects.bulk_update(cables, ['_abs_length'], batch_size=100)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0143_remove_primary_for_related_name'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=recalculate_abs_length,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 59 - 0
netbox/dcim/migrations/0145_site_remove_deprecated_fields.py

@@ -0,0 +1,59 @@
+import os
+
+from django.db import migrations
+from django.db.utils import DataError
+
+
+def check_legacy_data(apps, schema_editor):
+    """
+    Abort the migration if any legacy site fields still contain data.
+    """
+    Site = apps.get_model('dcim', 'Site')
+
+    site_count = Site.objects.exclude(asn__isnull=True).count()
+    if site_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
+        raise DataError(
+            f"Unable to proceed with deleting asn field from Site model: Found {site_count} sites with "
+            f"legacy ASN data. Please ensure all legacy site ASN data has been migrated to ASN objects "
+            f"before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA environment variable to bypass "
+            f"this safeguard and delete all legacy site ASN data."
+        )
+
+    site_count = Site.objects.exclude(contact_name='', contact_phone='', contact_email='').count()
+    if site_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
+        raise DataError(
+            f"Unable to proceed with deleting contact fields from Site model: Found {site_count} sites "
+            f"with legacy contact data. Please ensure all legacy site contact data has been migrated to "
+            f"contact objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA environment "
+            f"variable to bypass this safeguard and delete all legacy site contact data."
+        )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0144_fix_cable_abs_length'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=check_legacy_data,
+            reverse_code=migrations.RunPython.noop
+        ),
+        migrations.RemoveField(
+            model_name='site',
+            name='asn',
+        ),
+        migrations.RemoveField(
+            model_name='site',
+            name='contact_email',
+        ),
+        migrations.RemoveField(
+            model_name='site',
+            name='contact_name',
+        ),
+        migrations.RemoveField(
+            model_name='site',
+            name='contact_phone',
+        ),
+    ]

+ 279 - 0
netbox/dcim/migrations/0146_modules.py

@@ -0,0 +1,279 @@
+from utilities.json import CustomFieldJSONEncoder
+from django.db import migrations, models
+import django.db.models.deletion
+import taggit.managers
+import utilities.fields
+import utilities.ordering
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0066_customfield_name_validation'),
+        ('dcim', '0145_site_remove_deprecated_fields'),
+    ]
+
+    operations = [
+        # Rename any indexes left over from the old Module model (now InventoryItem) (#8656)
+        migrations.RunSQL(
+            """
+            DO $$
+            DECLARE
+                idx record;
+            BEGIN
+                FOR idx IN
+                    SELECT indexname AS old_name,
+                           replace(indexname, 'module', 'inventoryitem') AS new_name
+                    FROM pg_indexes
+                    WHERE schemaname = 'public' AND
+                          tablename = 'dcim_inventoryitem' AND
+                          indexname LIKE 'dcim_module_%'
+                LOOP
+                    EXECUTE format(
+                        'ALTER INDEX %I RENAME TO %I;',
+                        idx.old_name,
+                        idx.new_name
+                    );
+                END LOOP;
+            END$$;
+            """
+        ),
+
+        migrations.AlterModelOptions(
+            name='consoleporttemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterModelOptions(
+            name='consoleserverporttemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterModelOptions(
+            name='frontporttemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterModelOptions(
+            name='interfacetemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterModelOptions(
+            name='poweroutlettemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterModelOptions(
+            name='powerporttemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterModelOptions(
+            name='rearporttemplate',
+            options={'ordering': ('device_type', 'module_type', '_name')},
+        ),
+        migrations.AlterField(
+            model_name='consoleporttemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverporttemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AlterField(
+            model_name='frontporttemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlettemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AlterField(
+            model_name='powerporttemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.AlterField(
+            model_name='rearporttemplate',
+            name='device_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype'),
+        ),
+        migrations.CreateModel(
+            name='ModuleType',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('model', models.CharField(max_length=100)),
+                ('part_number', models.CharField(blank=True, max_length=50)),
+                ('comments', models.TextField(blank=True)),
+                ('manufacturer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='module_types', to='dcim.manufacturer')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'ordering': ('manufacturer', 'model'),
+                'unique_together': {('manufacturer', 'model')},
+            },
+        ),
+        migrations.CreateModel(
+            name='ModuleBay',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('position', models.CharField(blank=True, max_length=30)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.device')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'ordering': ('device', '_name'),
+                'unique_together': {('device', 'name')},
+            },
+        ),
+        migrations.CreateModel(
+            name='Module',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('local_context_data', models.JSONField(blank=True, null=True)),
+                ('serial', models.CharField(blank=True, max_length=50)),
+                ('asset_tag', models.CharField(blank=True, max_length=50, null=True, unique=True)),
+                ('comments', models.TextField(blank=True)),
+                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modules', to='dcim.device')),
+                ('module_bay', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='installed_module', to='dcim.modulebay')),
+                ('module_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='instances', to='dcim.moduletype')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'ordering': ('module_bay',),
+            },
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='consoleporttemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverporttemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='frontporttemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlettemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='powerporttemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='module',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.module'),
+        ),
+        migrations.AddField(
+            model_name='rearporttemplate',
+            name='module_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.moduletype'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleporttemplate',
+            unique_together={('device_type', 'name'), ('module_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverporttemplate',
+            unique_together={('device_type', 'name'), ('module_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='frontporttemplate',
+            unique_together={('device_type', 'name'), ('rear_port', 'rear_port_position'), ('module_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='interfacetemplate',
+            unique_together={('device_type', 'name'), ('module_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlettemplate',
+            unique_together={('device_type', 'name'), ('module_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerporttemplate',
+            unique_together={('device_type', 'name'), ('module_type', 'name')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='rearporttemplate',
+            unique_together={('device_type', 'name'), ('module_type', 'name')},
+        ),
+        migrations.CreateModel(
+            name='ModuleBayTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('position', models.CharField(blank=True, max_length=30)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype')),
+            ],
+            options={
+                'ordering': ('device_type', '_name'),
+                'unique_together': {('device_type', 'name')},
+            },
+        ),
+    ]

+ 38 - 0
netbox/dcim/migrations/0147_inventoryitemrole.py

@@ -0,0 +1,38 @@
+from utilities.json import CustomFieldJSONEncoder
+from django.db import migrations, models
+import django.db.models.deletion
+import taggit.managers
+import utilities.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0068_configcontext_cluster_types'),
+        ('dcim', '0146_modules'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='InventoryItemRole',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='role',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_items', to='dcim.inventoryitemrole'),
+        ),
+    ]

+ 23 - 0
netbox/dcim/migrations/0148_inventoryitem_component.py

@@ -0,0 +1,23 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('dcim', '0147_inventoryitemrole'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='component_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='component_type',
+            field=models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'poweroutlet', 'powerport', 'rearport'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype'),
+        ),
+    ]

+ 43 - 0
netbox/dcim/migrations/0149_inventoryitem_templates.py

@@ -0,0 +1,43 @@
+from django.db import migrations, models
+import django.db.models.deletion
+import mptt.fields
+import utilities.fields
+import utilities.ordering
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('dcim', '0148_inventoryitem_component'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='InventoryItemTemplate',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=64)),
+                ('_name', utilities.fields.NaturalOrderingField('name', blank=True, max_length=100, naturalize_function=utilities.ordering.naturalize)),
+                ('label', models.CharField(blank=True, max_length=64)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('component_id', models.PositiveBigIntegerField(blank=True, null=True)),
+                ('part_id', models.CharField(blank=True, max_length=50)),
+                ('lft', models.PositiveIntegerField(editable=False)),
+                ('rght', models.PositiveIntegerField(editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(editable=False)),
+                ('component_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(('app_label', 'dcim'), ('model__in', ('consoleporttemplate', 'consoleserverporttemplate', 'frontporttemplate', 'interfacetemplate', 'poweroutlettemplate', 'powerporttemplate', 'rearporttemplate'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
+                ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='dcim.devicetype')),
+                ('manufacturer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_templates', to='dcim.manufacturer')),
+                ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.inventoryitemtemplate')),
+                ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='inventory_item_templates', to='dcim.inventoryitemrole')),
+            ],
+            options={
+                'ordering': ('device_type__id', 'parent__id', '_name'),
+                'unique_together': {('device_type', 'parent', 'name')},
+            },
+        ),
+    ]

+ 20 - 0
netbox/dcim/migrations/0150_interface_vrf.py

@@ -0,0 +1,20 @@
+# Generated by Django 3.2.11 on 2022-01-07 18:34
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0054_vlangroup_min_max_vids'),
+        ('dcim', '0149_inventoryitem_templates'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='vrf',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces', to='ipam.vrf'),
+        ),
+    ]

+ 23 - 0
netbox/dcim/migrations/0151_interface_speed_duplex.py

@@ -0,0 +1,23 @@
+# Generated by Django 3.2.10 on 2022-01-08 18:23
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0150_interface_vrf'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='duplex',
+            field=models.CharField(blank=True, max_length=50, null=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='speed',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+    ]

+ 274 - 0
netbox/dcim/migrations/0152_standardize_id_fields.py

@@ -0,0 +1,274 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0151_interface_speed_duplex'),
+    ]
+
+    operations = [
+        # Model IDs
+        migrations.AlterField(
+            model_name='cable',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='cablepath',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='consoleport',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='consoleporttemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverport',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverporttemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='devicebay',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='devicebaytemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='devicerole',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='frontport',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='frontporttemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitemrole',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitemtemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='location',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='manufacturer',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='module',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='modulebay',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='modulebaytemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='moduletype',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='platform',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='powerfeed',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlet',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlettemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='powerpanel',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='powerport',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='powerporttemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='rackreservation',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='rackrole',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='rearport',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='rearporttemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='region',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='site',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='sitegroup',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='virtualchassis',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+
+        # GFK IDs
+        migrations.AlterField(
+            model_name='cable',
+            name='termination_a_id',
+            field=models.PositiveBigIntegerField(),
+        ),
+        migrations.AlterField(
+            model_name='cable',
+            name='termination_b_id',
+            field=models.PositiveBigIntegerField(),
+        ),
+        migrations.AlterField(
+            model_name='cablepath',
+            name='destination_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='cablepath',
+            name='origin_id',
+            field=models.PositiveBigIntegerField(),
+        ),
+        migrations.AlterField(
+            model_name='consoleport',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverport',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='frontport',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='powerfeed',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlet',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='powerport',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='rearport',
+            name='_link_peer_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+    ]

+ 208 - 0
netbox/dcim/migrations/0153_created_datetimefield.py

@@ -0,0 +1,208 @@
+# Generated by Django 4.0.2 on 2022-02-08 18:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0152_standardize_id_fields'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='cable',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleport',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleporttemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverport',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverporttemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='devicebay',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='devicebaytemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='devicerole',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='frontport',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='frontporttemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitemrole',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitemtemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='location',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='manufacturer',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='module',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='modulebay',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='modulebaytemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='moduletype',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='platform',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='powerfeed',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlet',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlettemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='powerpanel',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='powerport',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='powerporttemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='rackreservation',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='rackrole',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='rearport',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='rearporttemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='region',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='site',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='sitegroup',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='virtualchassis',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+    ]

+ 23 - 0
netbox/dcim/migrations/0154_half_height_rack_units.py

@@ -0,0 +1,23 @@
+import django.contrib.postgres.fields
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0153_created_datetimefield'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='devicetype',
+            name='u_height',
+            field=models.DecimalField(decimal_places=1, default=1.0, max_digits=4),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='position',
+            field=models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100.5)]),
+        ),
+    ]

+ 33 - 0
netbox/dcim/migrations/0155_interface_poe_mode_type.py

@@ -0,0 +1,33 @@
+# Generated by Django 4.0.5 on 2022-06-22 00:36
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0154_half_height_rack_units'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='poe_mode',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='poe_type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='poe_mode',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='poe_type',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+    ]

+ 18 - 0
netbox/dcim/migrations/0156_location_status.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.0.5 on 2022-06-22 17:10
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0155_interface_poe_mode_type'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='location',
+            name='status',
+            field=models.CharField(default='active', max_length=50),
+        ),
+    ]

+ 95 - 0
netbox/dcim/migrations/0157_new_cabling_models.py

@@ -0,0 +1,95 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('dcim', '0156_location_status'),
+    ]
+
+    operations = [
+
+        # Create CableTermination model
+        migrations.CreateModel(
+            name='CableTermination',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('cable_end', models.CharField(max_length=1)),
+                ('termination_id', models.PositiveBigIntegerField()),
+                ('cable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='dcim.cable')),
+                ('termination_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), models.Q(('app_label', 'dcim'), ('model__in', ('consoleport', 'consoleserverport', 'frontport', 'interface', 'powerfeed', 'poweroutlet', 'powerport', 'rearport'))), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
+                ('_device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.device')),
+                ('_rack', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.rack')),
+                ('_location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.location')),
+                ('_site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dcim.site')),
+            ],
+            options={
+                'ordering': ('cable', 'cable_end', 'pk'),
+            },
+        ),
+        migrations.AddConstraint(
+            model_name='cabletermination',
+            constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='dcim_cable_termination_unique_termination'),
+        ),
+
+        # Update CablePath model
+        migrations.RenameField(
+            model_name='cablepath',
+            old_name='path',
+            new_name='_nodes',
+        ),
+        migrations.AddField(
+            model_name='cablepath',
+            name='path',
+            field=models.JSONField(default=list),
+        ),
+        migrations.AddField(
+            model_name='cablepath',
+            name='is_complete',
+            field=models.BooleanField(default=False),
+        ),
+
+        # Add cable_end field to cable termination models
+        migrations.AddField(
+            model_name='consoleport',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='cable_end',
+            field=models.CharField(blank=True, max_length=1),
+        ),
+    ]

+ 87 - 0
netbox/dcim/migrations/0158_populate_cable_terminations.py

@@ -0,0 +1,87 @@
+import sys
+
+from django.db import migrations
+
+
+def cache_related_objects(termination):
+    """
+    Replicate caching logic from CableTermination.cache_related_objects()
+    """
+    attrs = {}
+
+    # Device components
+    if getattr(termination, 'device', None):
+        attrs['_device'] = termination.device
+        attrs['_rack'] = termination.device.rack
+        attrs['_location'] = termination.device.location
+        attrs['_site'] = termination.device.site
+
+    # Power feeds
+    elif getattr(termination, 'rack', None):
+        attrs['_rack'] = termination.rack
+        attrs['_location'] = termination.rack.location
+        attrs['_site'] = termination.rack.site
+
+    # Circuit terminations
+    elif getattr(termination, 'site', None):
+        attrs['_site'] = termination.site
+
+    return attrs
+
+
+def populate_cable_terminations(apps, schema_editor):
+    """
+    Replicate terminations from the Cable model into CableTermination instances.
+    """
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    Cable = apps.get_model('dcim', 'Cable')
+    CableTermination = apps.get_model('dcim', 'CableTermination')
+
+    # Retrieve the necessary data from Cable objects
+    cables = Cable.objects.values(
+        'id', 'termination_a_type', 'termination_a_id', 'termination_b_type', 'termination_b_id'
+    )
+
+    # Queue CableTerminations to be created
+    cable_terminations = []
+    cable_count = cables.count()
+    for i, cable in enumerate(cables, start=1):
+        for cable_end in ('a', 'b'):
+            # We must manually instantiate the termination object, because GFK fields are not
+            # supported within migrations.
+            termination_ct = ContentType.objects.get(pk=cable[f'termination_{cable_end}_type'])
+            termination_model = apps.get_model(termination_ct.app_label, termination_ct.model)
+            termination = termination_model.objects.get(pk=cable[f'termination_{cable_end}_id'])
+
+            cable_terminations.append(CableTermination(
+                cable_id=cable['id'],
+                cable_end=cable_end.upper(),
+                termination_type_id=cable[f'termination_{cable_end}_type'],
+                termination_id=cable[f'termination_{cable_end}_id'],
+                **cache_related_objects(termination)
+            ))
+
+        # Output progress occasionally
+        if 'test' not in sys.argv and not i % 100:
+            progress = float(i) * 100 / cable_count
+            if i == 100:
+                print('')
+            sys.stdout.write(f"\r    Updated {i}/{cable_count} cables ({progress:.2f}%)")
+            sys.stdout.flush()
+
+    # Bulk create the termination objects
+    CableTermination.objects.bulk_create(cable_terminations, batch_size=100)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0157_new_cabling_models'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=populate_cable_terminations,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 50 - 0
netbox/dcim/migrations/0159_populate_cable_paths.py

@@ -0,0 +1,50 @@
+from django.db import migrations
+
+from dcim.utils import compile_path_node
+
+
+def populate_cable_paths(apps, schema_editor):
+    """
+    Replicate terminations from the Cable model into CableTermination instances.
+    """
+    CablePath = apps.get_model('dcim', 'CablePath')
+
+    # Construct the new two-dimensional path, and add the origin & destination objects to the nodes list
+    cable_paths = []
+    for cablepath in CablePath.objects.all():
+
+        # Origin
+        origin = compile_path_node(cablepath.origin_type_id, cablepath.origin_id)
+        cablepath.path.append([origin])
+        cablepath._nodes.insert(0, origin)
+
+        # Transit nodes
+        cablepath.path.extend([
+            [node] for node in cablepath._nodes[1:]
+        ])
+
+        # Destination
+        if cablepath.destination_id:
+            destination = compile_path_node(cablepath.destination_type_id, cablepath.destination_id)
+            cablepath.path.append([destination])
+            cablepath._nodes.append(destination)
+            cablepath.is_complete = True
+
+        cable_paths.append(cablepath)
+
+    # Bulk update all CableTerminations
+    CablePath.objects.bulk_update(cable_paths, fields=('path', '_nodes', 'is_complete'), batch_size=100)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0158_populate_cable_terminations'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=populate_cable_paths,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 46 - 0
netbox/dcim/migrations/0160_populate_cable_ends.py

@@ -0,0 +1,46 @@
+from django.db import migrations
+
+
+def populate_cable_terminations(apps, schema_editor):
+    Cable = apps.get_model('dcim', 'Cable')
+
+    cable_termination_models = (
+        apps.get_model('dcim', 'ConsolePort'),
+        apps.get_model('dcim', 'ConsoleServerPort'),
+        apps.get_model('dcim', 'PowerPort'),
+        apps.get_model('dcim', 'PowerOutlet'),
+        apps.get_model('dcim', 'Interface'),
+        apps.get_model('dcim', 'FrontPort'),
+        apps.get_model('dcim', 'RearPort'),
+        apps.get_model('dcim', 'PowerFeed'),
+        apps.get_model('circuits', 'CircuitTermination'),
+    )
+
+    for model in cable_termination_models:
+        model.objects.filter(
+            id__in=Cable.objects.filter(
+                termination_a_type__app_label=model._meta.app_label,
+                termination_a_type__model=model._meta.model_name
+            ).values_list('termination_a_id', flat=True)
+        ).update(cable_end='A')
+        model.objects.filter(
+            id__in=Cable.objects.filter(
+                termination_b_type__app_label=model._meta.app_label,
+                termination_b_type__model=model._meta.model_name
+            ).values_list('termination_b_id', flat=True)
+        ).update(cable_end='B')
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0037_new_cabling_models'),
+        ('dcim', '0159_populate_cable_paths'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=populate_cable_terminations,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 134 - 0
netbox/dcim/migrations/0161_cabling_cleanup.py

@@ -0,0 +1,134 @@
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0160_populate_cable_ends'),
+    ]
+
+    operations = [
+
+        # Remove old fields from Cable
+        migrations.AlterModelOptions(
+            name='cable',
+            options={'ordering': ('pk',)},
+        ),
+        migrations.AlterUniqueTogether(
+            name='cable',
+            unique_together=set(),
+        ),
+        migrations.RemoveField(
+            model_name='cable',
+            name='termination_a_id',
+        ),
+        migrations.RemoveField(
+            model_name='cable',
+            name='termination_a_type',
+        ),
+        migrations.RemoveField(
+            model_name='cable',
+            name='termination_b_id',
+        ),
+        migrations.RemoveField(
+            model_name='cable',
+            name='termination_b_type',
+        ),
+        migrations.RemoveField(
+            model_name='cable',
+            name='_termination_a_device',
+        ),
+        migrations.RemoveField(
+            model_name='cable',
+            name='_termination_b_device',
+        ),
+
+        # Remove old fields from CablePath
+        migrations.AlterUniqueTogether(
+            name='cablepath',
+            unique_together=set(),
+        ),
+        migrations.RemoveField(
+            model_name='cablepath',
+            name='destination_id',
+        ),
+        migrations.RemoveField(
+            model_name='cablepath',
+            name='destination_type',
+        ),
+        migrations.RemoveField(
+            model_name='cablepath',
+            name='origin_id',
+        ),
+        migrations.RemoveField(
+            model_name='cablepath',
+            name='origin_type',
+        ),
+
+        # Remove link peer type/ID fields from cable termination models
+        migrations.RemoveField(
+            model_name='consoleport',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='consoleport',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='consoleserverport',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='consoleserverport',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='frontport',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='frontport',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='interface',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='interface',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='powerfeed',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='powerfeed',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='poweroutlet',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='poweroutlet',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='powerport',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='powerport',
+            name='_link_peer_type',
+        ),
+        migrations.RemoveField(
+            model_name='rearport',
+            name='_link_peer_id',
+        ),
+        migrations.RemoveField(
+            model_name='rearport',
+            name='_link_peer_type',
+        ),
+
+    ]

+ 332 - 0
netbox/dcim/migrations/0162_unique_constraints.py

@@ -0,0 +1,332 @@
+from django.db import migrations, models
+import django.db.models.functions.text
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0161_cabling_cleanup'),
+    ]
+
+    operations = [
+        migrations.RemoveConstraint(
+            model_name='cabletermination',
+            name='dcim_cable_termination_unique_termination',
+        ),
+        migrations.RemoveConstraint(
+            model_name='location',
+            name='dcim_location_name',
+        ),
+        migrations.RemoveConstraint(
+            model_name='location',
+            name='dcim_location_slug',
+        ),
+        migrations.RemoveConstraint(
+            model_name='region',
+            name='dcim_region_name',
+        ),
+        migrations.RemoveConstraint(
+            model_name='region',
+            name='dcim_region_slug',
+        ),
+        migrations.RemoveConstraint(
+            model_name='sitegroup',
+            name='dcim_sitegroup_name',
+        ),
+        migrations.RemoveConstraint(
+            model_name='sitegroup',
+            name='dcim_sitegroup_slug',
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleport',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleporttemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverport',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverporttemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='device',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicebay',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicebaytemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicetype',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='frontport',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='frontporttemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='interface',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='interfacetemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='inventoryitem',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='inventoryitemtemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='modulebay',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='modulebaytemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='moduletype',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerfeed',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlet',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlettemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerpanel',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerport',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerporttemplate',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='rack',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='rearport',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='rearporttemplate',
+            unique_together=set(),
+        ),
+        migrations.AddConstraint(
+            model_name='cabletermination',
+            constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='dcim_cabletermination_unique_termination'),
+        ),
+        migrations.AddConstraint(
+            model_name='consoleport',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleport_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='consoleporttemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleporttemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='consoleporttemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleporttemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='consoleserverport',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_consoleserverport_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='consoleserverporttemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_consoleserverporttemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='consoleserverporttemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_consoleserverporttemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='device',
+            constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('site'), models.F('tenant'), name='dcim_device_unique_name_site_tenant'),
+        ),
+        migrations.AddConstraint(
+            model_name='device',
+            constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('site'), condition=models.Q(('tenant__isnull', True)), name='dcim_device_unique_name_site', violation_error_message='Device name must be unique per site.'),
+        ),
+        migrations.AddConstraint(
+            model_name='device',
+            constraint=models.UniqueConstraint(fields=('rack', 'position', 'face'), name='dcim_device_unique_rack_position_face'),
+        ),
+        migrations.AddConstraint(
+            model_name='device',
+            constraint=models.UniqueConstraint(fields=('virtual_chassis', 'vc_position'), name='dcim_device_unique_virtual_chassis_vc_position'),
+        ),
+        migrations.AddConstraint(
+            model_name='devicebay',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_devicebay_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='devicebaytemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_devicebaytemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='devicetype',
+            constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_devicetype_unique_manufacturer_model'),
+        ),
+        migrations.AddConstraint(
+            model_name='devicetype',
+            constraint=models.UniqueConstraint(fields=('manufacturer', 'slug'), name='dcim_devicetype_unique_manufacturer_slug'),
+        ),
+        migrations.AddConstraint(
+            model_name='frontport',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_frontport_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='frontport',
+            constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontport_unique_rear_port_position'),
+        ),
+        migrations.AddConstraint(
+            model_name='frontporttemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_frontporttemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='frontporttemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_frontporttemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='frontporttemplate',
+            constraint=models.UniqueConstraint(fields=('rear_port', 'rear_port_position'), name='dcim_frontporttemplate_unique_rear_port_position'),
+        ),
+        migrations.AddConstraint(
+            model_name='interface',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_interface_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='interfacetemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_interfacetemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='interfacetemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_interfacetemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='inventoryitem',
+            constraint=models.UniqueConstraint(fields=('device', 'parent', 'name'), name='dcim_inventoryitem_unique_device_parent_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='inventoryitemtemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'parent', 'name'), name='dcim_inventoryitemtemplate_unique_device_type_parent_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='location',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'name'), name='dcim_location_name', violation_error_message='A location with this name already exists within the specified site.'),
+        ),
+        migrations.AddConstraint(
+            model_name='location',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('site', 'slug'), name='dcim_location_slug', violation_error_message='A location with this slug already exists within the specified site.'),
+        ),
+        migrations.AddConstraint(
+            model_name='modulebay',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_modulebay_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='modulebaytemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_modulebaytemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='moduletype',
+            constraint=models.UniqueConstraint(fields=('manufacturer', 'model'), name='dcim_moduletype_unique_manufacturer_model'),
+        ),
+        migrations.AddConstraint(
+            model_name='powerfeed',
+            constraint=models.UniqueConstraint(fields=('power_panel', 'name'), name='dcim_powerfeed_unique_power_panel_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='poweroutlet',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_poweroutlet_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='poweroutlettemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_poweroutlettemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='poweroutlettemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_poweroutlettemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='powerpanel',
+            constraint=models.UniqueConstraint(fields=('site', 'name'), name='dcim_powerpanel_unique_site_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='powerport',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_powerport_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='powerporttemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_powerporttemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='powerporttemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_powerporttemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='rack',
+            constraint=models.UniqueConstraint(fields=('location', 'name'), name='dcim_rack_unique_location_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='rack',
+            constraint=models.UniqueConstraint(fields=('location', 'facility_id'), name='dcim_rack_unique_location_facility_id'),
+        ),
+        migrations.AddConstraint(
+            model_name='rearport',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_rearport_unique_device_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='rearporttemplate',
+            constraint=models.UniqueConstraint(fields=('device_type', 'name'), name='dcim_rearporttemplate_unique_device_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='rearporttemplate',
+            constraint=models.UniqueConstraint(fields=('module_type', 'name'), name='dcim_rearporttemplate_unique_module_type_name'),
+        ),
+        migrations.AddConstraint(
+            model_name='region',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_region_name', violation_error_message='A top-level region with this name already exists.'),
+        ),
+        migrations.AddConstraint(
+            model_name='region',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('slug',), name='dcim_region_slug', violation_error_message='A top-level region with this slug already exists.'),
+        ),
+        migrations.AddConstraint(
+            model_name='sitegroup',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('name',), name='dcim_sitegroup_name', violation_error_message='A top-level site group with this name already exists.'),
+        ),
+        migrations.AddConstraint(
+            model_name='sitegroup',
+            constraint=models.UniqueConstraint(condition=models.Q(('parent__isnull', True)), fields=('slug',), name='dcim_sitegroup_slug', violation_error_message='A top-level site group with this slug already exists.'),
+        ),
+    ]

+ 72 - 0
netbox/dcim/migrations/0163_weight_fields.py

@@ -0,0 +1,72 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0162_unique_constraints'),
+    ]
+
+    operations = [
+
+        # Device types
+        migrations.AddField(
+            model_name='devicetype',
+            name='weight',
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='weight_unit',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='_abs_weight',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+
+        # Module types
+        migrations.AddField(
+            model_name='moduletype',
+            name='weight',
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
+        ),
+        migrations.AddField(
+            model_name='moduletype',
+            name='weight_unit',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='moduletype',
+            name='_abs_weight',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+
+        # Racks
+        migrations.AddField(
+            model_name='rack',
+            name='weight',
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='max_weight',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='weight_unit',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='_abs_weight',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='_abs_max_weight',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+    ]

+ 18 - 0
netbox/dcim/migrations/0164_rack_mounting_depth.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.1 on 2022-10-27 14:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0163_weight_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='rack',
+            name='mounting_depth',
+            field=models.PositiveSmallIntegerField(blank=True, null=True),
+        ),
+    ]

+ 78 - 0
netbox/dcim/migrations/0165_standardize_description_comments.py

@@ -0,0 +1,78 @@
+# Generated by Django 4.1.2 on 2022-11-03 18:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0164_rack_mounting_depth'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='cable',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='cable',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='module',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='moduletype',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='powerpanel',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+        migrations.AddField(
+            model_name='rackreservation',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='virtualchassis',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='virtualchassis',
+            name='description',
+            field=models.CharField(blank=True, max_length=200),
+        ),
+    ]

+ 54 - 0
netbox/dcim/migrations/0166_virtualdevicecontext.py

@@ -0,0 +1,54 @@
+# Generated by Django 4.1.2 on 2022-11-10 16:56
+
+from django.db import migrations, models
+import django.db.models.deletion
+import taggit.managers
+import utilities.json
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ipam', '0063_standardize_description_comments'),
+        ('extras', '0082_savedfilter'),
+        ('tenancy', '0009_standardize_description_comments'),
+        ('dcim', '0165_standardize_description_comments'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='VirtualDeviceContext',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('name', models.CharField(max_length=64)),
+                ('status', models.CharField(max_length=50)),
+                ('identifier', models.PositiveSmallIntegerField(blank=True, null=True)),
+                ('comments', models.TextField(blank=True)),
+                ('device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vdcs', to='dcim.device')),
+                ('primary_ip4', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress')),
+                ('primary_ip6', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='ipam.ipaddress')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+                ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vdcs', to='tenancy.tenant')),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='vdcs',
+            field=models.ManyToManyField(related_name='interfaces', to='dcim.virtualdevicecontext'),
+        ),
+        migrations.AddConstraint(
+            model_name='virtualdevicecontext',
+            constraint=models.UniqueConstraint(fields=('device', 'identifier'), name='dcim_virtualdevicecontext_device_identifier'),
+        ),
+        migrations.AddConstraint(
+            model_name='virtualdevicecontext',
+            constraint=models.UniqueConstraint(fields=('device', 'name'), name='dcim_virtualdevicecontext_device_name'),
+        ),
+    ]

+ 18 - 0
netbox/dcim/migrations/0167_module_status.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2022-12-09 15:09
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0166_virtualdevicecontext'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='module',
+            name='status',
+            field=models.CharField(default='active', max_length=50),
+        ),
+    ]

+ 22 - 0
netbox/dcim/migrations/0168_interface_template_enabled.py

@@ -0,0 +1,22 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0167_module_status'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='enabled',
+            field=models.BooleanField(default=True),
+        ),
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='bridge',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interfacetemplate'),
+        ),
+    ]

+ 19 - 0
netbox/dcim/migrations/0169_devicetype_default_platform.py

@@ -0,0 +1,19 @@
+# Generated by Django 4.1.6 on 2023-02-10 18:06
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0168_interface_template_enabled'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='devicetype',
+            name='default_platform',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.platform'),
+        ),
+    ]

+ 28 - 0
netbox/dcim/migrations/0170_configtemplate.py

@@ -0,0 +1,28 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0086_configtemplate'),
+        ('dcim', '0169_devicetype_default_platform'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='device',
+            name='config_template',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(class)ss', to='extras.configtemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicerole',
+            name='config_template',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='device_roles', to='extras.configtemplate'),
+        ),
+        migrations.AddField(
+            model_name='platform',
+            name='config_template',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='extras.configtemplate'),
+        ),
+    ]

+ 21 - 0
netbox/dcim/migrations/0171_cabletermination_change_logging.py

@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0170_configtemplate'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='cabletermination',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='cabletermination',
+            name='last_updated',
+            field=models.DateTimeField(auto_now=True, null=True),
+        ),
+    ]

+ 42 - 0
netbox/dcim/migrations/0172_larger_power_draw_values.py

@@ -0,0 +1,42 @@
+# Generated by Django 4.1.9 on 2023-05-12 18:46
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0171_cabletermination_change_logging'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='powerport',
+            name='allocated_draw',
+            field=models.PositiveIntegerField(
+                blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]
+            ),
+        ),
+        migrations.AlterField(
+            model_name='powerport',
+            name='maximum_draw',
+            field=models.PositiveIntegerField(
+                blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]
+            ),
+        ),
+        migrations.AlterField(
+            model_name='powerporttemplate',
+            name='allocated_draw',
+            field=models.PositiveIntegerField(
+                blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]
+            ),
+        ),
+        migrations.AlterField(
+            model_name='powerporttemplate',
+            name='maximum_draw',
+            field=models.PositiveIntegerField(
+                blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]
+            ),
+        ),
+    ]

+ 19 - 0
netbox/dcim/migrations/0173_remove_napalm_fields.py

@@ -0,0 +1,19 @@
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0172_larger_power_draw_values'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='platform',
+            name='napalm_args',
+        ),
+        migrations.RemoveField(
+            model_name='platform',
+            name='napalm_driver',
+        ),
+    ]

+ 22 - 0
netbox/dcim/migrations/0174_device_latitude_device_longitude.py

@@ -0,0 +1,22 @@
+# Generated by Django 4.1.9 on 2023-05-31 22:13
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0173_remove_napalm_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='device',
+            name='latitude',
+            field=models.DecimalField(blank=True, decimal_places=6, max_digits=8, null=True),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='longitude',
+            field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
+        ),
+    ]

+ 18 - 0
netbox/dcim/migrations/0174_rack_starting_unit.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.9 on 2023-05-31 15:47
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0174_device_latitude_device_longitude'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='rack',
+            name='starting_unit',
+            field=models.PositiveSmallIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)]),
+        ),
+    ]

+ 25 - 0
netbox/dcim/migrations/0175_device_oob_ip.py

@@ -0,0 +1,25 @@
+# Generated by Django 4.1.9 on 2023-07-24 20:29
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('ipam', '0066_iprange_mark_utilized'),
+        ('dcim', '0174_rack_starting_unit'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='device',
+            name='oob_ip',
+            field=models.OneToOneField(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name='+',
+                to='ipam.ipaddress',
+            ),
+        ),
+    ]

+ 83 - 0
netbox/dcim/migrations/0176_device_component_counters.py

@@ -0,0 +1,83 @@
+from django.db import migrations
+from django.db.models import Count
+
+import utilities.fields
+from utilities.counters import update_counts
+
+
+def recalculate_device_counts(apps, schema_editor):
+    Device = apps.get_model("dcim", "Device")
+
+    update_counts(Device, 'console_port_count', 'consoleports')
+    update_counts(Device, 'console_server_port_count', 'consoleserverports')
+    update_counts(Device, 'power_port_count', 'powerports')
+    update_counts(Device, 'power_outlet_count', 'poweroutlets')
+    update_counts(Device, 'interface_count', 'interfaces')
+    update_counts(Device, 'front_port_count', 'frontports')
+    update_counts(Device, 'rear_port_count', 'rearports')
+    update_counts(Device, 'device_bay_count', 'devicebays')
+    update_counts(Device, 'module_bay_count', 'modulebays')
+    update_counts(Device, 'inventory_item_count', 'inventoryitems')
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0175_device_oob_ip'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='device',
+            name='console_port_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.ConsolePort'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='console_server_port_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.ConsoleServerPort'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='power_port_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.PowerPort'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='power_outlet_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.PowerOutlet'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='interface_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.Interface'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='front_port_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.FrontPort'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='rear_port_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.RearPort'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='device_bay_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.DeviceBay'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='module_bay_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.ModuleBay'),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='inventory_item_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device', to_model='dcim.InventoryItem'),
+        ),
+        migrations.RunPython(
+            recalculate_device_counts,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 83 - 0
netbox/dcim/migrations/0177_devicetype_component_counters.py

@@ -0,0 +1,83 @@
+from django.db import migrations
+from django.db.models import Count
+
+import utilities.fields
+from utilities.counters import update_counts
+
+
+def recalculate_devicetype_template_counts(apps, schema_editor):
+    DeviceType = apps.get_model("dcim", "DeviceType")
+
+    update_counts(DeviceType, 'console_port_template_count', 'consoleporttemplates')
+    update_counts(DeviceType, 'console_server_port_template_count', 'consoleserverporttemplates')
+    update_counts(DeviceType, 'power_port_template_count', 'powerporttemplates')
+    update_counts(DeviceType, 'power_outlet_template_count', 'poweroutlettemplates')
+    update_counts(DeviceType, 'interface_template_count', 'interfacetemplates')
+    update_counts(DeviceType, 'front_port_template_count', 'frontporttemplates')
+    update_counts(DeviceType, 'rear_port_template_count', 'rearporttemplates')
+    update_counts(DeviceType, 'device_bay_template_count', 'devicebaytemplates')
+    update_counts(DeviceType, 'module_bay_template_count', 'modulebaytemplates')
+    update_counts(DeviceType, 'inventory_item_template_count', 'inventoryitemtemplates')
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0176_device_component_counters'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='devicetype',
+            name='console_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ConsolePortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='console_server_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ConsoleServerPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='power_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.PowerPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='power_outlet_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.PowerOutletTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='interface_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.InterfaceTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='front_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.FrontPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='rear_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.RearPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='device_bay_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.DeviceBayTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='module_bay_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ModuleBayTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='inventory_item_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.InventoryItemTemplate'),
+        ),
+        migrations.RunPython(
+            recalculate_devicetype_template_counts,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 31 - 0
netbox/dcim/migrations/0178_virtual_chassis_member_counter.py

@@ -0,0 +1,31 @@
+from django.db import migrations
+from django.db.models import Count
+
+import utilities.fields
+from utilities.counters import update_counts
+
+
+def populate_virtualchassis_members(apps, schema_editor):
+    VirtualChassis = apps.get_model('dcim', 'VirtualChassis')
+
+    update_counts(VirtualChassis, 'member_count', 'members')
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0177_devicetype_component_counters'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='virtualchassis',
+            name='member_count',
+            field=utilities.fields.CounterCacheField(
+                default=0, to_field='virtual_chassis', to_model='dcim.Device'
+            ),
+        ),
+        migrations.RunPython(
+            code=populate_virtualchassis_members,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 18 - 0
netbox/dcim/migrations/0179_interfacetemplate_rf_role.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.2.2 on 2023-07-18 07:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0178_virtual_chassis_member_counter'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='interfacetemplate',
+            name='rf_role',
+            field=models.CharField(blank=True, max_length=30),
+        ),
+    ]

+ 20 - 0
netbox/dcim/migrations/0180_powerfeed_tenant.py

@@ -0,0 +1,20 @@
+# Generated by Django 4.1.8 on 2023-07-29 11:29
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tenancy', '0010_tenant_relax_uniqueness'),
+        ('dcim', '0179_interfacetemplate_rf_role'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='powerfeed',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='power_feeds', to='tenancy.tenant'),
+        ),
+    ]

+ 35 - 0
netbox/dcim/migrations/0181_rename_device_role_device_role.py

@@ -0,0 +1,35 @@
+from django.db import migrations
+
+
+def update_table_configs(apps, schema_editor):
+    """
+    Replace the `device_role` column in DeviceTable configs with `role`.
+    """
+    UserConfig = apps.get_model('users', 'UserConfig')
+
+    for table in ('DeviceTable', 'DeviceBayTable'):
+        for config in UserConfig.objects.filter(**{f'data__tables__{table}__columns__contains': 'device_role'}):
+            config.data['tables'][table]['columns'] = [
+                'role' if x == 'device_role' else x
+                for x in config.data['tables'][table]['columns']
+            ]
+            config.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0180_powerfeed_tenant'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='device',
+            old_name='device_role',
+            new_name='role',
+        ),
+        migrations.RunPython(
+            code=update_table_configs,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 22 - 0
netbox/dcim/migrations/0182_zero_length_cable_fix.py

@@ -0,0 +1,22 @@
+from django.db import migrations
+
+
+def update_cable_lengths(apps, schema_editor):
+    Cable = apps.get_model('dcim', 'Cable')
+
+    # Set the absolute length for any zero-length Cables
+    Cable.objects.filter(length=0).update(_abs_length=0)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0181_rename_device_role_device_role'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=update_cable_lengths,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 0 - 453
netbox/extras/migrations/0001_initial.py

@@ -1,453 +0,0 @@
-from django.conf import settings
-import django.contrib.postgres.fields
-import django.core.validators
-from django.db import migrations, models
-import django.db.models.deletion
-import extras.fields
-import extras.models.customfields
-import extras.models.mixins
-import extras.utils
-import re
-import taggit.managers
-import utilities.fields
-import utilities.json
-import utilities.validators
-import uuid
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('core', '0001_initial'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('contenttypes', '0002_remove_content_type_name'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Report',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-            ],
-            options={
-                'managed': False,
-            },
-        ),
-        migrations.CreateModel(
-            name='Script',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-            ],
-            options={
-                'managed': False,
-            },
-        ),
-        migrations.CreateModel(
-            name='Bookmark',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True)),
-                ('object_id', models.PositiveBigIntegerField()),
-            ],
-            options={
-                'verbose_name': 'bookmark',
-                'verbose_name_plural': 'bookmarks',
-                'ordering': ('created', 'pk'),
-            },
-        ),
-        migrations.CreateModel(
-            name='Branch',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-            ],
-            options={
-                'verbose_name': 'branch',
-                'verbose_name_plural': 'branches',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='CachedValue',
-            fields=[
-                ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
-                ('timestamp', models.DateTimeField(auto_now_add=True)),
-                ('object_id', models.PositiveBigIntegerField()),
-                ('field', models.CharField(max_length=200)),
-                ('type', models.CharField(max_length=30)),
-                ('value', extras.fields.CachedValueField()),
-                ('weight', models.PositiveSmallIntegerField(default=1000)),
-            ],
-            options={
-                'verbose_name': 'cached value',
-                'verbose_name_plural': 'cached values',
-                'ordering': ('weight', 'object_type', 'object_id'),
-            },
-        ),
-        migrations.CreateModel(
-            name='ConfigContext',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('data_path', models.CharField(blank=True, editable=False, max_length=1000)),
-                ('auto_sync_enabled', models.BooleanField(default=False)),
-                ('data_synced', models.DateTimeField(blank=True, editable=False, null=True)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('weight', models.PositiveSmallIntegerField(default=1000)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('is_active', models.BooleanField(default=True)),
-                ('data', models.JSONField()),
-            ],
-            options={
-                'verbose_name': 'config context',
-                'verbose_name_plural': 'config contexts',
-                'ordering': ['weight', 'name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='ConfigRevision',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True)),
-                ('comment', models.CharField(blank=True, max_length=200)),
-                ('data', models.JSONField(blank=True, null=True)),
-            ],
-            options={
-                'verbose_name': 'config revision',
-                'verbose_name_plural': 'config revisions',
-                'ordering': ['-created'],
-            },
-        ),
-        migrations.CreateModel(
-            name='CustomFieldChoiceSet',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('base_choices', models.CharField(blank=True, max_length=50)),
-                ('extra_choices', django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), size=2), blank=True, null=True, size=None)),
-                ('order_alphabetically', models.BooleanField(default=False)),
-            ],
-            options={
-                'verbose_name': 'custom field choice set',
-                'verbose_name_plural': 'custom field choice sets',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='Tag',
-            fields=[
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(allow_unicode=True, max_length=100, unique=True)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('id', models.BigAutoField(primary_key=True, serialize=False)),
-                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('object_types', models.ManyToManyField(blank=True, related_name='+', to='contenttypes.contenttype')),
-            ],
-            options={
-                'verbose_name': 'tag',
-                'verbose_name_plural': 'tags',
-                'ordering': ['name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='TaggedItem',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('object_id', models.IntegerField(db_index=True)),
-                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_tagged_items', to='contenttypes.contenttype')),
-                ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='extras.tag')),
-            ],
-            options={
-                'verbose_name': 'tagged item',
-                'verbose_name_plural': 'tagged items',
-            },
-        ),
-        migrations.CreateModel(
-            name='ReportModule',
-            fields=[
-            ],
-            options={
-                'verbose_name': 'report module',
-                'verbose_name_plural': 'report modules',
-                'proxy': True,
-                'indexes': [],
-                'constraints': [],
-            },
-            bases=(extras.models.mixins.PythonModuleMixin, 'core.managedfile', models.Model),
-        ),
-        migrations.CreateModel(
-            name='ScriptModule',
-            fields=[
-            ],
-            options={
-                'verbose_name': 'script module',
-                'verbose_name_plural': 'script modules',
-                'proxy': True,
-                'indexes': [],
-                'constraints': [],
-            },
-            bases=(extras.models.mixins.PythonModuleMixin, 'core.managedfile', models.Model),
-        ),
-        migrations.CreateModel(
-            name='Webhook',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('name', models.CharField(max_length=150, unique=True)),
-                ('type_create', models.BooleanField(default=False)),
-                ('type_update', models.BooleanField(default=False)),
-                ('type_delete', models.BooleanField(default=False)),
-                ('type_job_start', models.BooleanField(default=False)),
-                ('type_job_end', models.BooleanField(default=False)),
-                ('payload_url', models.CharField(max_length=500)),
-                ('enabled', models.BooleanField(default=True)),
-                ('http_method', models.CharField(default='POST', max_length=30)),
-                ('http_content_type', models.CharField(default='application/json', max_length=100)),
-                ('additional_headers', models.TextField(blank=True)),
-                ('body_template', models.TextField(blank=True)),
-                ('secret', models.CharField(blank=True, max_length=255)),
-                ('conditions', models.JSONField(blank=True, null=True)),
-                ('ssl_verification', models.BooleanField(default=True)),
-                ('ca_file_path', models.CharField(blank=True, max_length=4096, null=True)),
-                ('content_types', models.ManyToManyField(related_name='webhooks', to='contenttypes.contenttype')),
-                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
-            ],
-            options={
-                'verbose_name': 'webhook',
-                'verbose_name_plural': 'webhooks',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='StagedChange',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('action', models.CharField(max_length=20)),
-                ('object_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('data', models.JSONField(blank=True, null=True)),
-                ('branch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staged_changes', to='extras.branch')),
-                ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')),
-            ],
-            options={
-                'verbose_name': 'staged change',
-                'verbose_name_plural': 'staged changes',
-                'ordering': ('pk',),
-            },
-        ),
-        migrations.CreateModel(
-            name='SavedFilter',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('slug', models.SlugField(max_length=100, unique=True)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('weight', models.PositiveSmallIntegerField(default=100)),
-                ('enabled', models.BooleanField(default=True)),
-                ('shared', models.BooleanField(default=True)),
-                ('parameters', models.JSONField()),
-                ('content_types', models.ManyToManyField(related_name='saved_filters', to='contenttypes.contenttype')),
-                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
-            ],
-            options={
-                'verbose_name': 'saved filter',
-                'verbose_name_plural': 'saved filters',
-                'ordering': ('weight', 'name'),
-            },
-        ),
-        migrations.CreateModel(
-            name='ObjectChange',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('time', models.DateTimeField(auto_now_add=True, db_index=True)),
-                ('user_name', models.CharField(editable=False, max_length=150)),
-                ('request_id', models.UUIDField(db_index=True, editable=False)),
-                ('action', models.CharField(max_length=50)),
-                ('changed_object_id', models.PositiveBigIntegerField()),
-                ('related_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('object_repr', models.CharField(editable=False, max_length=200)),
-                ('prechange_data', models.JSONField(blank=True, editable=False, null=True)),
-                ('postchange_data', models.JSONField(blank=True, editable=False, null=True)),
-                ('changed_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
-                ('related_object_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
-                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='changes', to=settings.AUTH_USER_MODEL)),
-            ],
-            options={
-                'verbose_name': 'object change',
-                'verbose_name_plural': 'object changes',
-                'ordering': ['-time'],
-            },
-        ),
-        migrations.CreateModel(
-            name='JournalEntry',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('assigned_object_id', models.PositiveBigIntegerField()),
-                ('kind', models.CharField(default='info', max_length=30)),
-                ('comments', models.TextField()),
-                ('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
-                ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
-                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
-            ],
-            options={
-                'verbose_name': 'journal entry',
-                'verbose_name_plural': 'journal entries',
-                'ordering': ('-created',),
-            },
-        ),
-        migrations.CreateModel(
-            name='ImageAttachment',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('object_id', models.PositiveBigIntegerField()),
-                ('image', models.ImageField(height_field='image_height', upload_to=extras.utils.image_upload, width_field='image_width')),
-                ('image_height', models.PositiveSmallIntegerField()),
-                ('image_width', models.PositiveSmallIntegerField()),
-                ('name', models.CharField(blank=True, max_length=50)),
-                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
-            ],
-            options={
-                'verbose_name': 'image attachment',
-                'verbose_name_plural': 'image attachments',
-                'ordering': ('name', 'pk'),
-            },
-        ),
-        migrations.CreateModel(
-            name='ExportTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('data_path', models.CharField(blank=True, editable=False, max_length=1000)),
-                ('auto_sync_enabled', models.BooleanField(default=False)),
-                ('data_synced', models.DateTimeField(blank=True, editable=False, null=True)),
-                ('name', models.CharField(max_length=100)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('template_code', models.TextField()),
-                ('mime_type', models.CharField(blank=True, max_length=50)),
-                ('file_extension', models.CharField(blank=True, max_length=15)),
-                ('as_attachment', models.BooleanField(default=True)),
-                ('content_types', models.ManyToManyField(related_name='export_templates', to='contenttypes.contenttype')),
-                ('data_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile')),
-                ('data_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource')),
-            ],
-            options={
-                'verbose_name': 'export template',
-                'verbose_name_plural': 'export templates',
-                'ordering': ('name',),
-            },
-        ),
-        migrations.CreateModel(
-            name='Dashboard',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('layout', models.JSONField(default=list)),
-                ('config', models.JSONField(default=dict)),
-                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dashboard', to=settings.AUTH_USER_MODEL)),
-            ],
-            options={
-                'verbose_name': 'dashboard',
-                'verbose_name_plural': 'dashboards',
-            },
-        ),
-        migrations.CreateModel(
-            name='CustomLink',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('name', models.CharField(max_length=100, unique=True)),
-                ('enabled', models.BooleanField(default=True)),
-                ('link_text', models.TextField()),
-                ('link_url', models.TextField()),
-                ('weight', models.PositiveSmallIntegerField(default=100)),
-                ('group_name', models.CharField(blank=True, max_length=50)),
-                ('button_class', models.CharField(default='outline-dark', max_length=30)),
-                ('new_window', models.BooleanField(default=False)),
-                ('content_types', models.ManyToManyField(related_name='custom_links', to='contenttypes.contenttype')),
-            ],
-            options={
-                'verbose_name': 'custom link',
-                'verbose_name_plural': 'custom links',
-                'ordering': ['group_name', 'weight', 'name'],
-            },
-        ),
-        migrations.CreateModel(
-            name='CustomField',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('type', models.CharField(default='text', max_length=50)),
-                ('name', models.CharField(max_length=50, unique=True, validators=[django.core.validators.RegexValidator(flags=re.RegexFlag['IGNORECASE'], message='Only alphanumeric characters and underscores are allowed.', regex='^[a-z0-9_]+$'), django.core.validators.RegexValidator(flags=re.RegexFlag['IGNORECASE'], inverse_match=True, message='Double underscores are not permitted in custom field names.', regex='__')])),
-                ('label', models.CharField(blank=True, max_length=50)),
-                ('group_name', models.CharField(blank=True, max_length=50)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('required', models.BooleanField(default=False)),
-                ('search_weight', models.PositiveSmallIntegerField(default=1000)),
-                ('filter_logic', models.CharField(default='loose', max_length=50)),
-                ('default', models.JSONField(blank=True, null=True)),
-                ('weight', models.PositiveSmallIntegerField(default=100)),
-                ('validation_minimum', models.IntegerField(blank=True, null=True)),
-                ('validation_maximum', models.IntegerField(blank=True, null=True)),
-                ('validation_regex', models.CharField(blank=True, max_length=500, validators=[utilities.validators.validate_regex])),
-                ('ui_visibility', models.CharField(default='read-write', max_length=50)),
-                ('is_cloneable', models.BooleanField(default=False)),
-                ('choice_set', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='choices_for', to='extras.customfieldchoiceset')),
-                ('content_types', models.ManyToManyField(related_name='custom_fields', to='contenttypes.contenttype')),
-                ('object_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
-            ],
-            options={
-                'verbose_name': 'custom field',
-                'verbose_name_plural': 'custom fields',
-                'ordering': ['group_name', 'weight', 'name'],
-            },
-            managers=[
-                ('objects', extras.models.customfields.CustomFieldManager()),
-            ],
-        ),
-        migrations.CreateModel(
-            name='ConfigTemplate',
-            fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
-                ('created', models.DateTimeField(auto_now_add=True, null=True)),
-                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
-                ('data_path', models.CharField(blank=True, editable=False, max_length=1000)),
-                ('auto_sync_enabled', models.BooleanField(default=False)),
-                ('data_synced', models.DateTimeField(blank=True, editable=False, null=True)),
-                ('name', models.CharField(max_length=100)),
-                ('description', models.CharField(blank=True, max_length=200)),
-                ('template_code', models.TextField()),
-                ('environment_params', models.JSONField(blank=True, default=dict, null=True)),
-                ('data_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile')),
-                ('data_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource')),
-                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
-            ],
-            options={
-                'verbose_name': 'config template',
-                'verbose_name_plural': 'config templates',
-                'ordering': ('name',),
-            },
-        ),
-    ]

+ 235 - 0
netbox/extras/migrations/0001_squashed.py

@@ -0,0 +1,235 @@
+from django.conf import settings
+import django.contrib.postgres.fields
+from django.db import migrations, models
+import django.db.models.deletion
+import extras.models.customfields
+import extras.utils
+import utilities.fields
+import utilities.validators
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('contenttypes', '0002_remove_content_type_name'),
+    ]
+
+    replaces = [
+        ('extras', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Report',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
+            ],
+            options={
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Script',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
+            ],
+            options={
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='ConfigContext',
+            fields=[
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('weight', models.PositiveSmallIntegerField(default=1000)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('is_active', models.BooleanField(default=True)),
+                ('data', models.JSONField()),
+            ],
+            options={
+                'ordering': ['weight', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Tag',
+            fields=[
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('slug', models.SlugField(max_length=100, unique=True)),
+                ('created', models.DateField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('color', utilities.fields.ColorField(default='9e9e9e', max_length=6)),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Webhook',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=150, unique=True)),
+                ('type_create', models.BooleanField(default=False)),
+                ('type_update', models.BooleanField(default=False)),
+                ('type_delete', models.BooleanField(default=False)),
+                ('payload_url', models.CharField(max_length=500)),
+                ('enabled', models.BooleanField(default=True)),
+                ('http_method', models.CharField(default='POST', max_length=30)),
+                ('http_content_type', models.CharField(default='application/json', max_length=100)),
+                ('additional_headers', models.TextField(blank=True)),
+                ('body_template', models.TextField(blank=True)),
+                ('secret', models.CharField(blank=True, max_length=255)),
+                ('ssl_verification', models.BooleanField(default=True)),
+                ('ca_file_path', models.CharField(blank=True, max_length=4096, null=True)),
+                ('content_types', models.ManyToManyField(related_name='webhooks', to='contenttypes.ContentType')),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.CreateModel(
+            name='TaggedItem',
+            fields=[
+                ('object_id', models.IntegerField(db_index=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_tagged_items', to='contenttypes.contenttype')),
+                ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='extras.tag')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ObjectChange',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('time', models.DateTimeField(auto_now_add=True, db_index=True)),
+                ('user_name', models.CharField(editable=False, max_length=150)),
+                ('request_id', models.UUIDField(editable=False)),
+                ('action', models.CharField(max_length=50)),
+                ('changed_object_id', models.PositiveIntegerField()),
+                ('related_object_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('object_repr', models.CharField(editable=False, max_length=200)),
+                ('prechange_data', models.JSONField(blank=True, editable=False, null=True)),
+                ('postchange_data', models.JSONField(blank=True, editable=False, null=True)),
+                ('changed_object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
+                ('related_object_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
+                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='changes', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['-time'],
+            },
+        ),
+        migrations.CreateModel(
+            name='JournalEntry',
+            fields=[
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('assigned_object_id', models.PositiveIntegerField()),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('kind', models.CharField(default='info', max_length=30)),
+                ('comments', models.TextField()),
+                ('assigned_object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
+                ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'verbose_name_plural': 'journal entries',
+                'ordering': ('-created',),
+            },
+        ),
+        migrations.CreateModel(
+            name='JobResult',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=255)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('completed', models.DateTimeField(blank=True, null=True)),
+                ('status', models.CharField(default='pending', max_length=30)),
+                ('data', models.JSONField(blank=True, null=True)),
+                ('job_id', models.UUIDField(unique=True)),
+                ('obj_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='job_results', to='contenttypes.contenttype')),
+                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['obj_type', 'name', '-created'],
+            },
+        ),
+        migrations.CreateModel(
+            name='ImageAttachment',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('object_id', models.PositiveIntegerField()),
+                ('image', models.ImageField(height_field='image_height', upload_to=extras.utils.image_upload, width_field='image_width')),
+                ('image_height', models.PositiveSmallIntegerField()),
+                ('image_width', models.PositiveSmallIntegerField()),
+                ('name', models.CharField(blank=True, max_length=50)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
+            ],
+            options={
+                'ordering': ('name', 'pk'),
+            },
+        ),
+        migrations.CreateModel(
+            name='ExportTemplate',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('template_code', models.TextField()),
+                ('mime_type', models.CharField(blank=True, max_length=50)),
+                ('file_extension', models.CharField(blank=True, max_length=15)),
+                ('as_attachment', models.BooleanField(default=True)),
+                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
+            ],
+            options={
+                'ordering': ['content_type', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='CustomLink',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=100, unique=True)),
+                ('link_text', models.CharField(max_length=500)),
+                ('link_url', models.CharField(max_length=500)),
+                ('weight', models.PositiveSmallIntegerField(default=100)),
+                ('group_name', models.CharField(blank=True, max_length=50)),
+                ('button_class', models.CharField(default='default', max_length=30)),
+                ('new_window', models.BooleanField(default=False)),
+                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
+            ],
+            options={
+                'ordering': ['group_name', 'weight', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='CustomField',
+            fields=[
+                ('id', models.BigAutoField(primary_key=True, serialize=False)),
+                ('type', models.CharField(default='text', max_length=50)),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('label', models.CharField(blank=True, max_length=50)),
+                ('description', models.CharField(blank=True, max_length=200)),
+                ('required', models.BooleanField(default=False)),
+                ('filter_logic', models.CharField(default='loose', max_length=50)),
+                ('default', models.JSONField(blank=True, null=True)),
+                ('weight', models.PositiveSmallIntegerField(default=100)),
+                ('validation_minimum', models.PositiveIntegerField(blank=True, null=True)),
+                ('validation_maximum', models.PositiveIntegerField(blank=True, null=True)),
+                ('validation_regex', models.CharField(blank=True, max_length=500, validators=[utilities.validators.validate_regex])),
+                ('choices', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), blank=True, null=True, size=None)),
+                ('content_types', models.ManyToManyField(related_name='custom_fields', to='contenttypes.ContentType')),
+            ],
+            options={
+                'ordering': ['weight', 'name'],
+            },
+            managers=[
+                ('objects', extras.models.customfields.CustomFieldManager()),
+            ],
+        ),
+    ]

+ 0 - 228
netbox/extras/migrations/0002_squashed.py

@@ -1,228 +0,0 @@
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('core', '0002_squashed'),
-        ('extras', '0001_initial'),
-        ('virtualization', '0001_initial'),
-        ('contenttypes', '0002_remove_content_type_name'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('dcim', '0003_squashed'),
-        ('tenancy', '0001_initial'),
-    ]
-
-    replaces = [
-        ('extras', '0002_custom_fields'),
-        ('extras', '0003_exporttemplate_add_description'),
-        ('extras', '0004_topologymap_change_comma_to_semicolon'),
-        ('extras', '0005_useraction_add_bulk_create'),
-        ('extras', '0006_add_imageattachments'),
-        ('extras', '0007_unicode_literals'),
-        ('extras', '0008_reports'),
-        ('extras', '0009_topologymap_type'),
-        ('extras', '0010_customfield_filter_logic'),
-        ('extras', '0011_django2'),
-        ('extras', '0012_webhooks'),
-        ('extras', '0013_objectchange'),
-        ('extras', '0014_configcontexts'),
-        ('extras', '0015_remove_useraction'),
-        ('extras', '0016_exporttemplate_add_cable'),
-        ('extras', '0017_exporttemplate_mime_type_length'),
-        ('extras', '0018_exporttemplate_add_jinja2'),
-        ('extras', '0019_tag_taggeditem'),
-        ('extras', '0020_tag_data'),
-        ('extras', '0021_add_color_comments_changelog_to_tag'),
-        ('extras', '0022_custom_links'),
-        ('extras', '0023_fix_tag_sequences'),
-        ('extras', '0024_scripts'),
-        ('extras', '0025_objectchange_time_index'),
-        ('extras', '0026_webhook_ca_file_path'),
-        ('extras', '0027_webhook_additional_headers'),
-        ('extras', '0028_remove_topology_maps'),
-        ('extras', '0029_3569_customfield_fields'),
-        ('extras', '0030_3569_objectchange_fields'),
-        ('extras', '0031_3569_exporttemplate_fields'),
-        ('extras', '0032_3569_webhook_fields'),
-        ('extras', '0033_graph_type_template_language'),
-        ('extras', '0034_configcontext_tags'),
-        ('extras', '0035_deterministic_ordering'),
-        ('extras', '0036_contenttype_filters_to_q_objects'),
-        ('extras', '0037_configcontexts_clusters'),
-        ('extras', '0038_webhook_template_support'),
-        ('extras', '0039_update_features_content_types'),
-        ('extras', '0040_standardize_description'),
-        ('extras', '0041_tag_description'),
-        ('extras', '0042_customfield_manager'),
-        ('extras', '0043_report'),
-        ('extras', '0044_jobresult'),
-        ('extras', '0045_configcontext_changelog'),
-        ('extras', '0046_update_jsonfield'),
-        ('extras', '0047_tag_ordering'),
-        ('extras', '0048_exporttemplate_remove_template_language'),
-        ('extras', '0049_remove_graph'),
-        ('extras', '0050_customfield_changes'),
-        ('extras', '0051_migrate_customfields'),
-        ('extras', '0052_customfield_cleanup'),
-        ('extras', '0053_rename_webhook_obj_type'),
-        ('extras', '0054_standardize_models'),
-        ('extras', '0055_objectchange_data'),
-        ('extras', '0056_extend_configcontext'),
-        ('extras', '0057_customlink_rename_fields'),
-        ('extras', '0058_journalentry'),
-        ('extras', '0059_exporttemplate_as_attachment'),
-        ('extras', '0060_customlink_button_class'),
-        ('extras', '0061_extras_change_logging'),
-        ('extras', '0062_clear_secrets_changelog'),
-        ('extras', '0063_webhook_conditions'),
-        ('extras', '0064_configrevision'),
-        ('extras', '0065_imageattachment_change_logging'),
-        ('extras', '0066_customfield_name_validation'),
-        ('extras', '0067_customfield_min_max_values'),
-        ('extras', '0068_configcontext_cluster_types'),
-        ('extras', '0069_custom_object_field'),
-        ('extras', '0070_customlink_enabled'),
-        ('extras', '0071_standardize_id_fields'),
-        ('extras', '0072_created_datetimefield'),
-        ('extras', '0073_journalentry_tags_custom_fields'),
-        ('extras', '0074_customfield_extensions'),
-        ('extras', '0075_configcontext_locations'),
-        ('extras', '0076_tag_slug_unicode'),
-        ('extras', '0077_customlink_extend_text_and_url'),
-        ('extras', '0078_unique_constraints'),
-        ('extras', '0079_scheduled_jobs'),
-        ('extras', '0080_customlink_content_types'),
-        ('extras', '0081_exporttemplate_content_types'),
-        ('extras', '0082_savedfilter'),
-        ('extras', '0083_search'),
-        ('extras', '0084_staging'),
-        ('extras', '0085_synced_data'),
-        ('extras', '0086_configtemplate'),
-        ('extras', '0087_dashboard'),
-        ('extras', '0088_jobresult_webhooks'),
-        ('extras', '0089_customfield_is_cloneable'),
-        ('extras', '0090_objectchange_index_request_id'),
-        ('extras', '0091_create_managedfiles'),
-        ('extras', '0092_delete_jobresult'),
-        ('extras', '0093_configrevision_ordering'),
-        ('extras', '0094_tag_object_types'),
-        ('extras', '0095_bookmarks'),
-        ('extras', '0096_customfieldchoiceset'),
-        ('extras', '0097_customfield_remove_choices'),
-        ('extras', '0098_webhook_custom_field_data_webhook_tags'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='configcontext',
-            name='cluster_groups',
-            field=models.ManyToManyField(blank=True, related_name='+', to='virtualization.clustergroup'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='cluster_types',
-            field=models.ManyToManyField(blank=True, related_name='+', to='virtualization.clustertype'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='clusters',
-            field=models.ManyToManyField(blank=True, related_name='+', to='virtualization.cluster'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='data_file',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.datafile'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='data_source',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.datasource'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='device_types',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.devicetype'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='locations',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.location'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='platforms',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.platform'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='regions',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.region'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='roles',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.devicerole'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='site_groups',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.sitegroup'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='sites',
-            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.site'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='tags',
-            field=models.ManyToManyField(blank=True, related_name='+', to='extras.tag'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='tenant_groups',
-            field=models.ManyToManyField(blank=True, related_name='+', to='tenancy.tenantgroup'),
-        ),
-        migrations.AddField(
-            model_name='configcontext',
-            name='tenants',
-            field=models.ManyToManyField(blank=True, related_name='+', to='tenancy.tenant'),
-        ),
-        migrations.AddField(
-            model_name='cachedvalue',
-            name='object_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'),
-        ),
-        migrations.AddField(
-            model_name='branch',
-            name='user',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
-        ),
-        migrations.AddField(
-            model_name='bookmark',
-            name='object_type',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'),
-        ),
-        migrations.AddField(
-            model_name='bookmark',
-            name='user',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
-        ),
-        migrations.AddConstraint(
-            model_name='webhook',
-            constraint=models.UniqueConstraint(fields=('payload_url', 'type_create', 'type_update', 'type_delete'), name='extras_webhook_unique_payload_url_types'),
-        ),
-        migrations.AddIndex(
-            model_name='taggeditem',
-            index=models.Index(fields=['content_type', 'object_id'], name='extras_tagg_content_717743_idx'),
-        ),
-        migrations.AddConstraint(
-            model_name='bookmark',
-            constraint=models.UniqueConstraint(fields=('object_type', 'object_id', 'user'), name='extras_bookmark_unique_per_object_and_user'),
-        ),
-    ]

+ 142 - 0
netbox/extras/migrations/0002_squashed_0059.py

@@ -0,0 +1,142 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0002_auto_20160622_1821'),
+        ('extras', '0001_initial'),
+        ('virtualization', '0001_virtualization'),
+        ('tenancy', '0001_initial'),
+    ]
+
+    replaces = [
+        ('extras', '0002_custom_fields'),
+        ('extras', '0003_exporttemplate_add_description'),
+        ('extras', '0004_topologymap_change_comma_to_semicolon'),
+        ('extras', '0005_useraction_add_bulk_create'),
+        ('extras', '0006_add_imageattachments'),
+        ('extras', '0007_unicode_literals'),
+        ('extras', '0008_reports'),
+        ('extras', '0009_topologymap_type'),
+        ('extras', '0010_customfield_filter_logic'),
+        ('extras', '0011_django2'),
+        ('extras', '0012_webhooks'),
+        ('extras', '0013_objectchange'),
+        ('extras', '0014_configcontexts'),
+        ('extras', '0015_remove_useraction'),
+        ('extras', '0016_exporttemplate_add_cable'),
+        ('extras', '0017_exporttemplate_mime_type_length'),
+        ('extras', '0018_exporttemplate_add_jinja2'),
+        ('extras', '0019_tag_taggeditem'),
+        ('extras', '0020_tag_data'),
+        ('extras', '0021_add_color_comments_changelog_to_tag'),
+        ('extras', '0022_custom_links'),
+        ('extras', '0023_fix_tag_sequences'),
+        ('extras', '0024_scripts'),
+        ('extras', '0025_objectchange_time_index'),
+        ('extras', '0026_webhook_ca_file_path'),
+        ('extras', '0027_webhook_additional_headers'),
+        ('extras', '0028_remove_topology_maps'),
+        ('extras', '0029_3569_customfield_fields'),
+        ('extras', '0030_3569_objectchange_fields'),
+        ('extras', '0031_3569_exporttemplate_fields'),
+        ('extras', '0032_3569_webhook_fields'),
+        ('extras', '0033_graph_type_template_language'),
+        ('extras', '0034_configcontext_tags'),
+        ('extras', '0035_deterministic_ordering'),
+        ('extras', '0036_contenttype_filters_to_q_objects'),
+        ('extras', '0037_configcontexts_clusters'),
+        ('extras', '0038_webhook_template_support'),
+        ('extras', '0039_update_features_content_types'),
+        ('extras', '0040_standardize_description'),
+        ('extras', '0041_tag_description'),
+        ('extras', '0042_customfield_manager'),
+        ('extras', '0043_report'),
+        ('extras', '0044_jobresult'),
+        ('extras', '0045_configcontext_changelog'),
+        ('extras', '0046_update_jsonfield'),
+        ('extras', '0047_tag_ordering'),
+        ('extras', '0048_exporttemplate_remove_template_language'),
+        ('extras', '0049_remove_graph'),
+        ('extras', '0050_customfield_changes'),
+        ('extras', '0051_migrate_customfields'),
+        ('extras', '0052_customfield_cleanup'),
+        ('extras', '0053_rename_webhook_obj_type'),
+        ('extras', '0054_standardize_models'),
+        ('extras', '0055_objectchange_data'),
+        ('extras', '0056_extend_configcontext'),
+        ('extras', '0057_customlink_rename_fields'),
+        ('extras', '0058_journalentry'),
+        ('extras', '0059_exporttemplate_as_attachment'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='configcontext',
+            name='cluster_groups',
+            field=models.ManyToManyField(blank=True, related_name='+', to='virtualization.ClusterGroup'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='clusters',
+            field=models.ManyToManyField(blank=True, related_name='+', to='virtualization.Cluster'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='device_types',
+            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.DeviceType'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='platforms',
+            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.Platform'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='regions',
+            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.Region'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='roles',
+            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.DeviceRole'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='site_groups',
+            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.SiteGroup'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='sites',
+            field=models.ManyToManyField(blank=True, related_name='+', to='dcim.Site'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='tags',
+            field=models.ManyToManyField(blank=True, related_name='+', to='extras.Tag'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='tenant_groups',
+            field=models.ManyToManyField(blank=True, related_name='+', to='tenancy.TenantGroup'),
+        ),
+        migrations.AddField(
+            model_name='configcontext',
+            name='tenants',
+            field=models.ManyToManyField(blank=True, related_name='+', to='tenancy.Tenant'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='webhook',
+            unique_together={('payload_url', 'type_create', 'type_update', 'type_delete')},
+        ),
+        migrations.AlterIndexTogether(
+            name='taggeditem',
+            index_together={('content_type', 'object_id')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='exporttemplate',
+            unique_together={('content_type', 'name')},
+        ),
+    ]

+ 16 - 0
netbox/extras/migrations/0060_customlink_button_class.py

@@ -0,0 +1,16 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0059_exporttemplate_as_attachment'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='customlink',
+            name='button_class',
+            field=models.CharField(default='outline-dark', max_length=30),
+        ),
+    ]

+ 51 - 0
netbox/extras/migrations/0061_extras_change_logging.py

@@ -0,0 +1,51 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0060_customlink_button_class'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='customfield',
+            name='created',
+            field=models.DateField(auto_now_add=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='customfield',
+            name='last_updated',
+            field=models.DateTimeField(auto_now=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='customlink',
+            name='created',
+            field=models.DateField(auto_now_add=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='customlink',
+            name='last_updated',
+            field=models.DateTimeField(auto_now=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='exporttemplate',
+            name='created',
+            field=models.DateField(auto_now_add=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='exporttemplate',
+            name='last_updated',
+            field=models.DateTimeField(auto_now=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='webhook',
+            name='created',
+            field=models.DateField(auto_now_add=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='webhook',
+            name='last_updated',
+            field=models.DateTimeField(auto_now=True, null=True),
+        ),
+    ]

+ 26 - 0
netbox/extras/migrations/0062_clear_secrets_changelog.py

@@ -0,0 +1,26 @@
+from django.db import migrations
+
+
+def clear_secrets_changelog(apps, schema_editor):
+    """
+    Delete all ObjectChange records referencing a model within the old secrets app (pre-v3.0).
+    """
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    ObjectChange = apps.get_model('extras', 'ObjectChange')
+
+    content_type_ids = ContentType.objects.filter(app_label='secrets').values_list('id', flat=True)
+    ObjectChange.objects.filter(changed_object_type__in=content_type_ids).delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0061_extras_change_logging'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=clear_secrets_changelog,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 18 - 0
netbox/extras/migrations/0063_webhook_conditions.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.2.8 on 2021-10-22 20:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0062_clear_secrets_changelog'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='webhook',
+            name='conditions',
+            field=models.JSONField(blank=True, null=True),
+        ),
+    ]

+ 20 - 0
netbox/extras/migrations/0064_configrevision.py

@@ -0,0 +1,20 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0063_webhook_conditions'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ConfigRevision',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('comment', models.CharField(blank=True, max_length=200)),
+                ('data', models.JSONField(blank=True, null=True)),
+            ],
+        ),
+    ]

+ 16 - 0
netbox/extras/migrations/0065_imageattachment_change_logging.py

@@ -0,0 +1,16 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0064_configrevision'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='imageattachment',
+            name='last_updated',
+            field=models.DateTimeField(auto_now=True, null=True),
+        ),
+    ]

+ 34 - 0
netbox/extras/migrations/0066_customfield_name_validation.py

@@ -0,0 +1,34 @@
+import django.core.validators
+from django.db import migrations, models
+import re
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0065_imageattachment_change_logging'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='customfield',
+            name='name',
+            field=models.CharField(
+                max_length=50,
+                unique=True,
+                validators=[
+                    django.core.validators.RegexValidator(
+                        flags=re.RegexFlag['IGNORECASE'],
+                        message='Only alphanumeric characters and underscores are allowed.',
+                        regex='^[a-z0-9_]+$',
+                    ),
+                    django.core.validators.RegexValidator(
+                        flags=re.RegexFlag['IGNORECASE'],
+                        inverse_match=True,
+                        message='Double underscores are not permitted in custom field names.',
+                        regex=r'__',
+                    ),
+                ],
+            ),
+        ),
+    ]

+ 21 - 0
netbox/extras/migrations/0067_customfield_min_max_values.py

@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0066_customfield_name_validation'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='customfield',
+            name='validation_maximum',
+            field=models.IntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='customfield',
+            name='validation_minimum',
+            field=models.IntegerField(blank=True, null=True),
+        ),
+    ]

+ 18 - 0
netbox/extras/migrations/0068_configcontext_cluster_types.py

@@ -0,0 +1,18 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dcim', '0145_site_remove_deprecated_fields'),
+        ('virtualization', '0026_vminterface_bridge'),
+        ('extras', '0067_customfield_min_max_values'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='configcontext',
+            name='cluster_types',
+            field=models.ManyToManyField(blank=True, related_name='+', to='virtualization.ClusterType'),
+        ),
+    ]

+ 18 - 0
netbox/extras/migrations/0069_custom_object_field.py

@@ -0,0 +1,18 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('extras', '0068_configcontext_cluster_types'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='customfield',
+            name='object_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'),
+        ),
+    ]

+ 18 - 0
netbox/extras/migrations/0070_customlink_enabled.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.2.11 on 2022-01-10 16:45
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0069_custom_object_field'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='customlink',
+            name='enabled',
+            field=models.BooleanField(default=True),
+        ),
+    ]

+ 89 - 0
netbox/extras/migrations/0071_standardize_id_fields.py

@@ -0,0 +1,89 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0070_customlink_enabled'),
+    ]
+
+    operations = [
+        # Model IDs
+        migrations.AlterField(
+            model_name='configcontext',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='configrevision',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='customfield',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='customlink',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='exporttemplate',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='imageattachment',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='jobresult',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='journalentry',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='objectchange',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='taggeditem',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='webhook',
+            name='id',
+            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
+        ),
+
+        # GFK IDs
+        migrations.AlterField(
+            model_name='imageattachment',
+            name='object_id',
+            field=models.PositiveBigIntegerField(),
+        ),
+        migrations.AlterField(
+            model_name='journalentry',
+            name='assigned_object_id',
+            field=models.PositiveBigIntegerField(),
+        ),
+        migrations.AlterField(
+            model_name='objectchange',
+            name='changed_object_id',
+            field=models.PositiveBigIntegerField(),
+        ),
+        migrations.AlterField(
+            model_name='objectchange',
+            name='related_object_id',
+            field=models.PositiveBigIntegerField(blank=True, null=True),
+        ),
+    ]

+ 53 - 0
netbox/extras/migrations/0072_created_datetimefield.py

@@ -0,0 +1,53 @@
+# Generated by Django 4.0.2 on 2022-02-08 18:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0071_standardize_id_fields'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='configcontext',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='customfield',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='customlink',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='exporttemplate',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='imageattachment',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='journalentry',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='tag',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='webhook',
+            name='created',
+            field=models.DateTimeField(auto_now_add=True, null=True),
+        ),
+    ]

Некоторые файлы не были показаны из-за большого количества измененных файлов