Explorar el Código

Closes #2213: Added squashed migrations

Jeremy Stretch hace 7 años
padre
commit
4ae7f2337a

+ 96 - 0
netbox/circuits/migrations/0001_initial_squashed_0010_circuit_status.py

@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:25
+from __future__ import unicode_literals
+
+import dcim.fields
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('circuits', '0001_initial'), ('circuits', '0002_auto_20160622_1821'), ('circuits', '0003_provider_32bit_asn_support'), ('circuits', '0004_circuit_add_tenant'), ('circuits', '0005_circuit_add_upstream_speed'), ('circuits', '0006_terminations'), ('circuits', '0007_circuit_add_description'), ('circuits', '0008_circuittermination_interface_protect_on_delete'), ('circuits', '0009_unicode_literals'), ('circuits', '0010_circuit_status')]
+
+    dependencies = [
+        ('dcim', '0001_initial'),
+        ('dcim', '0022_color_names_to_rgb'),
+        ('tenancy', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Provider',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateField(auto_now_add=True)),
+                ('last_updated', models.DateTimeField(auto_now=True)),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+                ('asn', dcim.fields.ASNField(blank=True, null=True, verbose_name='ASN')),
+                ('account', models.CharField(blank=True, max_length=30, verbose_name='Account number')),
+                ('portal_url', models.URLField(blank=True, verbose_name='Portal')),
+                ('noc_contact', models.TextField(blank=True, verbose_name='NOC contact')),
+                ('admin_contact', models.TextField(blank=True, verbose_name='Admin contact')),
+                ('comments', models.TextField(blank=True)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='CircuitType',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Circuit',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateField(auto_now_add=True)),
+                ('last_updated', models.DateTimeField(auto_now=True)),
+                ('cid', models.CharField(max_length=50, verbose_name='Circuit ID')),
+                ('install_date', models.DateField(blank=True, null=True, verbose_name='Date installed')),
+                ('commit_rate', models.PositiveIntegerField(blank=True, null=True, verbose_name='Commit rate (Kbps)')),
+                ('comments', models.TextField(blank=True)),
+                ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.Provider')),
+                ('type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.CircuitType')),
+                ('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='tenancy.Tenant')),
+                ('description', models.CharField(blank=True, max_length=100)),
+                ('status', models.PositiveSmallIntegerField(choices=[[2, 'Planned'], [3, 'Provisioning'], [1, 'Active'], [4, 'Offline'], [0, 'Deprovisioning'], [5, 'Decommissioned']], default=1))
+            ],
+            options={
+                'ordering': ['provider', 'cid'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='circuit',
+            unique_together=set([('provider', 'cid')]),
+        ),
+        migrations.CreateModel(
+            name='CircuitTermination',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('term_side', models.CharField(choices=[('A', 'A'), ('Z', 'Z')], max_length=1, verbose_name='Termination')),
+                ('port_speed', models.PositiveIntegerField(verbose_name='Port speed (Kbps)')),
+                ('upstream_speed', models.PositiveIntegerField(blank=True, help_text='Upstream speed, if different from port speed', null=True, verbose_name='Upstream speed (Kbps)')),
+                ('xconnect_id', models.CharField(blank=True, max_length=50, verbose_name='Cross-connect ID')),
+                ('pp_info', models.CharField(blank=True, max_length=100, verbose_name='Patch panel/port(s)')),
+                ('circuit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='circuits.Circuit')),
+                ('interface', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_termination', to='dcim.Interface')),
+                ('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.Site')),
+            ],
+            options={
+                'ordering': ['circuit', 'term_side'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='circuittermination',
+            unique_together=set([('circuit', 'term_side')]),
+        ),
+    ]

+ 261 - 0
netbox/dcim/migrations/0002_auto_20160622_1821_squashed_0022_color_names_to_rgb.py

@@ -0,0 +1,261 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:06
+from __future__ import unicode_literals
+
+import dcim.fields
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import utilities.fields
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('dcim', '0002_auto_20160622_1821'), ('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')]
+
+    dependencies = [
+        ('dcim', '0001_initial'),
+        ('ipam', '0001_initial'),
+        ('tenancy', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='device',
+            name='rack',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.Rack'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cs_port_templates', to='dcim.DeviceType'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cs_ports', to='dcim.Device'),
+        ),
+        migrations.AddField(
+            model_name='consoleporttemplate',
+            name='device_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='console_port_templates', to='dcim.DeviceType'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='cs_port',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='connected_console', to='dcim.ConsoleServerPort', verbose_name=b'Console server port'),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='console_ports', to='dcim.Device'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='rackgroup',
+            unique_together=set([('site', 'name'), ('site', 'slug')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='rack',
+            unique_together=set([('site', 'facility_id'), ('site', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerporttemplate',
+            unique_together=set([('device_type', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='powerport',
+            unique_together=set([('device', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlettemplate',
+            unique_together=set([('device_type', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='poweroutlet',
+            unique_together=set([('device', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='module',
+            unique_together=set([('device', 'parent', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='interfacetemplate',
+            unique_together=set([('device_type', 'name')]),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='mac_address',
+            field=dcim.fields.MACAddressField(blank=True, null=True, verbose_name=b'MAC Address'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='interface',
+            unique_together=set([('device', 'name')]),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='subdevice_role',
+            field=models.NullBooleanField(choices=[(None, b'None'), (True, b'Parent'), (False, b'Child')], default=None, help_text=b'Parent devices house child devices in device bays. Select "None" if this device type is neither a parent nor a child.', verbose_name=b'Parent/child status'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicetype',
+            unique_together=set([('manufacturer', 'slug'), ('manufacturer', 'model')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='device',
+            unique_together=set([('rack', 'position', 'face')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverporttemplate',
+            unique_together=set([('device_type', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleserverport',
+            unique_together=set([('device', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleporttemplate',
+            unique_together=set([('device_type', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='consoleport',
+            unique_together=set([('device', 'name')]),
+        ),
+        migrations.CreateModel(
+            name='DeviceBay',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, verbose_name=b'Name')),
+                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='device_bays', to='dcim.Device')),
+                ('installed_device', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_bay', to='dcim.Device')),
+            ],
+            options={
+                'ordering': ['device', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='DeviceBayTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=30)),
+                ('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='device_bay_templates', to='dcim.DeviceType')),
+            ],
+            options={
+                'ordering': ['device_type', 'name'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicebaytemplate',
+            unique_together=set([('device_type', 'name')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='devicebay',
+            unique_together=set([('device', 'name')]),
+        ),
+        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', verbose_name=b'Primary IPv4'),
+        ),
+        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', verbose_name=b'Primary IPv6'),
+        ),
+        migrations.AlterField(
+            model_name='site',
+            name='asn',
+            field=dcim.fields.ASNField(blank=True, null=True, verbose_name=b'ASN'),
+        ),
+        migrations.AlterField(
+            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='devicetype',
+            name='part_number',
+            field=models.CharField(blank=True, help_text=b'Discrete part number (optional)', max_length=50),
+        ),
+        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='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='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='rack',
+            name='type',
+            field=models.PositiveSmallIntegerField(blank=True, choices=[(100, b'2-post frame'), (200, b'4-post frame'), (300, b'4-post cabinet'), (1000, b'Wall-mounted frame'), (1100, b'Wall-mounted cabinet')], null=True, verbose_name=b'Type'),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='width',
+            field=models.PositiveSmallIntegerField(choices=[(19, b'19 inches'), (23, b'23 inches')], default=19, help_text=b'Rail-to-rail width', verbose_name=b'Width'),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='u_height',
+            field=models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name=b'Height (U)'),
+        ),
+        migrations.AddField(
+            model_name='module',
+            name='manufacturer',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='modules', to='dcim.Manufacturer'),
+        ),
+        migrations.CreateModel(
+            name='RackRole',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+                ('color', utilities.fields.ColorField(max_length=6)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        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='device',
+            name='asset_tag',
+            field=utilities.fields.NullableCharField(blank=True, help_text=b'A unique tag used to identify this device', max_length=50, null=True, unique=True, verbose_name=b'Asset tag'),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='desc_units',
+            field=models.BooleanField(default=False, help_text=b'Units are numbered top-to-bottom', verbose_name=b'Descending units'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='position',
+            field=models.PositiveSmallIntegerField(blank=True, help_text=b'The lowest-numbered unit occupied by the device', null=True, validators=[django.core.validators.MinValueValidator(1)], verbose_name=b'Position (U)'),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='form_factor',
+            field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='form_factor',
+            field=models.PositiveSmallIntegerField(choices=[[b'Virtual interfaces', [[0, b'Virtual']]], [b'Ethernet (fixed)', [[800, b'100BASE-TX (10/100ME)'], [1000, b'1000BASE-T (1GE)'], [1150, b'10GBASE-T (10GE)']]], [b'Ethernet (modular)', [[1050, b'GBIC (1GE)'], [1100, b'SFP (1GE)'], [1200, b'SFP+ (10GE)'], [1300, b'XFP (10GE)'], [1310, b'XENPAK (10GE)'], [1320, b'X2 (10GE)'], [1350, b'SFP28 (25GE)'], [1400, b'QSFP+ (40GE)'], [1500, b'CFP (100GE)'], [1600, b'QSFP28 (100GE)']]], [b'FibreChannel', [[3010, b'SFP (1GFC)'], [3020, b'SFP (2GFC)'], [3040, b'SFP (4GFC)'], [3080, b'SFP+ (8GFC)'], [3160, b'SFP+ (16GFC)']]], [b'Serial', [[4000, b'T1 (1.544 Mbps)'], [4010, b'E1 (2.048 Mbps)'], [4040, b'T3 (45 Mbps)'], [4050, b'E3 (34 Mbps)']]], [b'Stacking', [[5000, b'Cisco StackWise'], [5050, b'Cisco StackWise Plus'], [5100, b'Cisco FlexStack'], [5150, b'Cisco FlexStack Plus']]], [b'Other', [[32767, b'Other']]]], default=1200),
+        ),
+        migrations.AlterField(
+            model_name='devicerole',
+            name='color',
+            field=utilities.fields.ColorField(max_length=6),
+        ),
+    ]

+ 435 - 0
netbox/dcim/migrations/0023_devicetype_comments_squashed_0043_device_component_name_lengths.py

@@ -0,0 +1,435 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:13
+from __future__ import unicode_literals
+
+import dcim.fields
+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 mptt.fields
+import utilities.fields
+
+
+def copy_site_from_rack(apps, schema_editor):
+    Device = apps.get_model('dcim', 'Device')
+    for device in Device.objects.all():
+        device.site = device.rack.site
+        device.save()
+
+
+def rpc_client_to_napalm_driver(apps, schema_editor):
+    """
+    Migrate legacy RPC clients to their respective NAPALM drivers
+    """
+    Platform = apps.get_model('dcim', 'Platform')
+
+    Platform.objects.filter(rpc_client='juniper-junos').update(napalm_driver='junos')
+    Platform.objects.filter(rpc_client='cisco-ios').update(napalm_driver='ios')
+
+class Migration(migrations.Migration):
+
+    replaces = [('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')]
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('dcim', '0022_color_names_to_rgb'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='devicetype',
+            name='comments',
+            field=models.TextField(blank=True),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='contact_email',
+            field=models.EmailField(blank=True, max_length=254, verbose_name=b'Contact E-mail'),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='contact_name',
+            field=models.CharField(blank=True, max_length=50),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='contact_phone',
+            field=models.CharField(blank=True, max_length=20),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='interface_ordering',
+            field=models.PositiveSmallIntegerField(choices=[[1, b'Slot/position'], [2, b'Name (alphabetically)']], default=1),
+        ),
+        migrations.CreateModel(
+            name='RackReservation',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('units', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(), size=None)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('description', models.CharField(max_length=100)),
+                ('rack', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='dcim.Rack')),
+                ('user', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['created'],
+            },
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='site',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='devices', to='dcim.Site'),
+        ),
+        migrations.RunPython(
+            code=copy_site_from_rack,
+        ),
+        migrations.AlterField(
+            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.AlterField(
+            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='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', verbose_name='Parent LAG'),
+        ),
+        migrations.CreateModel(
+            name='Region',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+                ('lft', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('rght', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.Region')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        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.AlterField(
+            model_name='device',
+            name='name',
+            field=utilities.fields.NullableCharField(blank=True, max_length=64, null=True, unique=True),
+        ),
+        migrations.AlterField(
+            model_name='rackreservation',
+            name='rack',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='dcim.Rack'),
+        ),
+        migrations.RenameModel(
+            old_name='Module',
+            new_name='InventoryItem',
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='device',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inventory_items', to='dcim.Device'),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='parent',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_items', to='dcim.InventoryItem'),
+        ),
+        migrations.AlterField(
+            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.AlterField(
+            model_name='device',
+            name='status',
+            field=models.PositiveIntegerField(choices=[[1, b'Active'], [0, b'Offline'], [2, b'Planned'], [3, b'Staged'], [4, b'Failed'], [5, b'Inventory']], default=1, verbose_name=b'Status'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [0, 'Offline'], [2, 'Planned'], [3, 'Staged'], [4, 'Failed'], [5, 'Inventory']], default=1, verbose_name='Status'),
+        ),
+        migrations.AlterField(
+            model_name='consoleport',
+            name='connection_status',
+            field=models.NullBooleanField(choices=[[False, 'Planned'], [True, 'Connected']], default=True),
+        ),
+        migrations.AlterField(
+            model_name='consoleport',
+            name='cs_port',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='connected_console', to='dcim.ConsoleServerPort', verbose_name='Console server port'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='asset_tag',
+            field=utilities.fields.NullableCharField(blank=True, help_text='A unique tag used to identify this device', max_length=50, null=True, unique=True, verbose_name='Asset tag'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='face',
+            field=models.PositiveSmallIntegerField(blank=True, choices=[[0, 'Front'], [1, 'Rear']], null=True, verbose_name='Rack face'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='position',
+            field=models.PositiveSmallIntegerField(blank=True, help_text='The lowest-numbered unit occupied by the device', null=True, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Position (U)'),
+        ),
+        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='primary_ip4_for', to='ipam.IPAddress', verbose_name='Primary IPv4'),
+        ),
+        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='primary_ip6_for', to='ipam.IPAddress', verbose_name='Primary IPv6'),
+        ),
+        migrations.AlterField(
+            model_name='device',
+            name='serial',
+            field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'),
+        ),
+        migrations.AlterField(
+            model_name='devicebay',
+            name='name',
+            field=models.CharField(max_length=50, verbose_name='Name'),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='interface_ordering',
+            field=models.PositiveSmallIntegerField(choices=[[1, 'Slot/position'], [2, 'Name (alphabetically)']], default=1),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='is_console_server',
+            field=models.BooleanField(default=False, help_text='This type of device has console server ports', verbose_name='Is a console server'),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='is_full_depth',
+            field=models.BooleanField(default=True, help_text='Device consumes both front and rear rack faces', verbose_name='Is full depth'),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='is_network_device',
+            field=models.BooleanField(default=True, help_text='This type of device has network interfaces', verbose_name='Is a network device'),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='is_pdu',
+            field=models.BooleanField(default=False, help_text='This type of device has power outlets', verbose_name='Is a PDU'),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='part_number',
+            field=models.CharField(blank=True, help_text='Discrete part number (optional)', max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='subdevice_role',
+            field=models.NullBooleanField(choices=[(None, 'None'), (True, 'Parent'), (False, 'Child')], default=None, help_text='Parent devices house child devices in device bays. Select "None" if this device type is neither a parent nor a child.', verbose_name='Parent/child status'),
+        ),
+        migrations.AlterField(
+            model_name='devicetype',
+            name='u_height',
+            field=models.PositiveSmallIntegerField(default=1, verbose_name='Height (U)'),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='mac_address',
+            field=dcim.fields.MACAddressField(blank=True, null=True, verbose_name='MAC Address'),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='mgmt_only',
+            field=models.BooleanField(default=False, help_text='This interface is used only for out-of-band management', verbose_name='OOB Management'),
+        ),
+        migrations.AlterField(
+            model_name='interfaceconnection',
+            name='connection_status',
+            field=models.BooleanField(choices=[[False, 'Planned'], [True, 'Connected']], default=True, verbose_name='Status'),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='mgmt_only',
+            field=models.BooleanField(default=False, verbose_name='Management only'),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='discovered',
+            field=models.BooleanField(default=False, verbose_name='Discovered'),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='name',
+            field=models.CharField(max_length=50, verbose_name='Name'),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='part_id',
+            field=models.CharField(blank=True, max_length=50, verbose_name='Part ID'),
+        ),
+        migrations.AlterField(
+            model_name='inventoryitem',
+            name='serial',
+            field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'),
+        ),
+        migrations.AlterField(
+            model_name='platform',
+            name='rpc_client',
+            field=models.CharField(blank=True, choices=[['juniper-junos', 'Juniper Junos (NETCONF)'], ['cisco-ios', 'Cisco IOS (SSH)'], ['opengear', 'Opengear (SSH)']], max_length=30, verbose_name='RPC client'),
+        ),
+        migrations.AlterField(
+            model_name='powerport',
+            name='connection_status',
+            field=models.NullBooleanField(choices=[[False, 'Planned'], [True, 'Connected']], default=True),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='desc_units',
+            field=models.BooleanField(default=False, help_text='Units are numbered top-to-bottom', verbose_name='Descending units'),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='facility_id',
+            field=utilities.fields.NullableCharField(blank=True, max_length=30, null=True, verbose_name='Facility ID'),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='type',
+            field=models.PositiveSmallIntegerField(blank=True, choices=[(100, '2-post frame'), (200, '4-post frame'), (300, '4-post cabinet'), (1000, 'Wall-mounted frame'), (1100, 'Wall-mounted cabinet')], null=True, verbose_name='Type'),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='u_height',
+            field=models.PositiveSmallIntegerField(default=42, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name='Height (U)'),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='width',
+            field=models.PositiveSmallIntegerField(choices=[(19, '19 inches'), (23, '23 inches')], default=19, help_text='Rail-to-rail width', verbose_name='Width'),
+        ),
+        migrations.AlterField(
+            model_name='site',
+            name='asn',
+            field=dcim.fields.ASNField(blank=True, null=True, verbose_name='ASN'),
+        ),
+        migrations.AlterField(
+            model_name='site',
+            name='contact_email',
+            field=models.EmailField(blank=True, max_length=254, verbose_name='Contact E-mail'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='enabled',
+            field=models.BooleanField(default=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='mtu',
+            field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='MTU'),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='asset_tag',
+            field=utilities.fields.NullableCharField(blank=True, help_text='A unique tag used to identify this item', max_length=50, null=True, unique=True, verbose_name='Asset tag'),
+        ),
+        migrations.AddField(
+            model_name='inventoryitem',
+            name='description',
+            field=models.CharField(blank=True, max_length=100),
+        ),
+        migrations.AlterModelOptions(
+            name='device',
+            options={'ordering': ['name'], 'permissions': (('napalm_read', 'Read-only access to devices via NAPALM'), ('napalm_write', 'Read/write access to devices via NAPALM'))},
+        ),
+        migrations.AddField(
+            model_name='platform',
+            name='napalm_driver',
+            field=models.CharField(blank=True, help_text='The name of the NAPALM driver to use when interacting with devices.', max_length=50, verbose_name='NAPALM driver'),
+        ),
+        migrations.AlterField(
+            model_name='platform',
+            name='rpc_client',
+            field=models.CharField(blank=True, choices=[['juniper-junos', 'Juniper Junos (NETCONF)'], ['cisco-ios', 'Cisco IOS (SSH)'], ['opengear', 'Opengear (SSH)']], max_length=30, verbose_name='Legacy RPC client'),
+        ),
+        migrations.RunPython(
+            code=rpc_client_to_napalm_driver,
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='form_factor',
+            field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='form_factor',
+            field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
+        ),
+        migrations.AlterField(
+            model_name='consoleport',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='consoleporttemplate',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverport',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='consoleserverporttemplate',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='devicebaytemplate',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='name',
+            field=models.CharField(max_length=64),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='name',
+            field=models.CharField(max_length=64),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlet',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='poweroutlettemplate',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='powerport',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='powerporttemplate',
+            name='name',
+            field=models.CharField(max_length=50),
+        ),
+    ]

+ 146 - 0
netbox/dcim/migrations/0044_virtualization_squashed_0055_virtualchassis_ordering.py

@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:17
+from __future__ import unicode_literals
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import timezone_field.fields
+import utilities.fields
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('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')]
+
+    dependencies = [
+        ('dcim', '0043_device_component_name_lengths'),
+        ('ipam', '0020_ipaddress_add_role_carp'),
+        ('virtualization', '0001_virtualization'),
+        ('tenancy', '0003_unicode_literals'),
+    ]
+
+    operations = [
+        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='interface',
+            name='virtual_machine',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='virtualization.VirtualMachine'),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='device',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='dcim.Device'),
+        ),
+        migrations.AddField(
+            model_name='devicerole',
+            name='vm_role',
+            field=models.BooleanField(default=True, help_text='Virtual machines may be assigned to this role', verbose_name='VM Role'),
+        ),
+        migrations.AlterField(
+            model_name='rack',
+            name='facility_id',
+            field=utilities.fields.NullableCharField(blank=True, max_length=50, null=True, verbose_name='Facility ID'),
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='form_factor',
+            field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
+        ),
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='form_factor',
+            field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP']]], ['Other', [[32767, 'Other']]]], default=1200),
+        ),
+        migrations.AddField(
+            model_name='rack',
+            name='serial',
+            field=models.CharField(blank=True, max_length=50, verbose_name='Serial number'),
+        ),
+        migrations.AlterField(
+            model_name='rackreservation',
+            name='user',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='mode',
+            field=models.PositiveSmallIntegerField(blank=True, choices=[[100, 'Access'], [200, 'Tagged'], [300, 'Tagged All']], null=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='tagged_vlans',
+            field=models.ManyToManyField(blank=True, related_name='interfaces_as_tagged', to='ipam.VLAN', verbose_name='Tagged VLANs'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='untagged_vlan',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interfaces_as_untagged', to='ipam.VLAN', verbose_name='Untagged VLAN'),
+        ),
+        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.CreateModel(
+            name='VirtualChassis',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('domain', models.CharField(blank=True, max_length=30)),
+                ('master', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='vc_master_for', to='dcim.Device')),
+            ],
+            options={
+                'verbose_name_plural': 'virtual chassis',
+                'ordering': ['master'],
+            },
+        ),
+        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='device',
+            name='vc_position',
+            field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)]),
+        ),
+        migrations.AddField(
+            model_name='device',
+            name='vc_priority',
+            field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MaxValueValidator(255)]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='device',
+            unique_together=set([('rack', 'position', 'face'), ('virtual_chassis', 'vc_position')]),
+        ),
+        migrations.AddField(
+            model_name='platform',
+            name='manufacturer',
+            field=models.ForeignKey(blank=True, help_text='Optionally limit this platform to devices of a certain manufacturer', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='platforms', to='dcim.Manufacturer'),
+        ),
+        migrations.AlterField(
+            model_name='platform',
+            name='napalm_driver',
+            field=models.CharField(blank=True, help_text='The name of the NAPALM driver to use when interacting with devices', max_length=50, verbose_name='NAPALM driver'),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='description',
+            field=models.CharField(blank=True, max_length=100),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [2, 'Planned'], [4, 'Retired']], default=1),
+        ),
+        migrations.AddField(
+            model_name='site',
+            name='time_zone',
+            field=timezone_field.fields.TimeZoneField(blank=True),
+        ),
+    ]

+ 193 - 0
netbox/extras/migrations/0001_initial_squashed_0010_customfield_filter_logic.py

@@ -0,0 +1,193 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:19
+from __future__ import unicode_literals
+
+import re
+from distutils.version import StrictVersion
+
+from django.conf import settings
+import django.contrib.postgres.fields.jsonb
+from django.db import connection, migrations, models
+import django.db.models.deletion
+import extras.models
+from django.db.utils import OperationalError
+
+from extras.constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_FILTER_LOOSE, CF_TYPE_SELECT
+
+
+def verify_postgresql_version(apps, schema_editor):
+    """
+    Verify that PostgreSQL is version 9.4 or higher.
+    """
+    try:
+        with connection.cursor() as cursor:
+            cursor.execute("SELECT VERSION()")
+            row = cursor.fetchone()
+            pg_version = re.match(r'^PostgreSQL (\d+\.\d+(\.\d+)?)', row[0]).group(1)
+            if StrictVersion(pg_version) < StrictVersion('9.4.0'):
+                raise Exception("PostgreSQL 9.4.0 or higher is required ({} found). Upgrade PostgreSQL and then run migrations again.".format(pg_version))
+
+    # Skip if the database is missing (e.g. for CI testing) or misconfigured.
+    except OperationalError:
+        pass
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('extras', '0001_initial'), ('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')]
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('dcim', '0002_auto_20160622_1821'),
+        ('contenttypes', '0002_remove_content_type_name'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ExportTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=100)),
+                ('template_code', models.TextField()),
+                ('mime_type', models.CharField(blank=True, max_length=15)),
+                ('file_extension', models.CharField(blank=True, max_length=15)),
+                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
+                ('description', models.CharField(blank=True, max_length=200)),
+            ],
+            options={
+                'ordering': ['content_type', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Graph',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('type', models.PositiveSmallIntegerField(choices=[(100, 'Interface'), (200, 'Provider'), (300, 'Site')])),
+                ('weight', models.PositiveSmallIntegerField(default=1000)),
+                ('name', models.CharField(max_length=100, verbose_name='Name')),
+                ('source', models.CharField(max_length=500, verbose_name='Source URL')),
+                ('link', models.URLField(blank=True, verbose_name='Link URL')),
+            ],
+            options={
+                'ordering': ['type', 'weight', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='TopologyMap',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+                ('device_patterns', models.TextField(help_text='Identify devices to include in the diagram using regular expressions, one per line. Each line will result in a new tier of the drawing. Separate multiple regexes within a line using semicolons. Devices will be rendered in the order they are defined.')),
+                ('description', models.CharField(blank=True, max_length=100)),
+                ('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='topology_maps', to='dcim.Site')),
+                ('type', models.PositiveSmallIntegerField(choices=[(1, 'Network'), (2, 'Console'), (3, 'Power')], default=1)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='UserAction',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('time', models.DateTimeField(auto_now_add=True)),
+                ('object_id', models.PositiveIntegerField(blank=True, null=True)),
+                ('action', models.PositiveSmallIntegerField(choices=[(1, 'created'), (7, 'bulk created'), (2, 'imported'), (3, 'modified'), (4, 'bulk edited'), (5, 'deleted'), (6, 'bulk deleted')])),
+                ('message', models.TextField(blank=True)),
+                ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='actions', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['-time'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='exporttemplate',
+            unique_together=set([('content_type', 'name')]),
+        ),
+        migrations.CreateModel(
+            name='CustomField',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('type', models.PositiveSmallIntegerField(choices=[(100, 'Text'), (200, 'Integer'), (300, 'Boolean (true/false)'), (400, 'Date'), (500, 'URL'), (600, 'Selection')], default=100)),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('label', models.CharField(blank=True, help_text="Name of the field as displayed to users (if not provided, the field's name will be used)", max_length=50)),
+                ('description', models.CharField(blank=True, max_length=100)),
+                ('required', models.BooleanField(default=False, help_text='If true, this field is required when creating new objects or editing an existing object.')),
+                ('default', models.CharField(blank=True, help_text='Default value for the field. Use "true" or "false" for booleans.', max_length=100)),
+                ('weight', models.PositiveSmallIntegerField(default=100, help_text='Fields with higher weights appear lower in a form.')),
+                ('obj_type', models.ManyToManyField(help_text='The object(s) to which this field applies.', related_name='custom_fields', to='contenttypes.ContentType', verbose_name='Object(s)')),
+                ('filter_logic', models.PositiveSmallIntegerField(choices=[(0, 'Disabled'), (1, 'Loose'), (2, 'Exact')], default=1, help_text='Loose matches any instance of a given string; exact matches the entire field.')),
+            ],
+            options={
+                'ordering': ['weight', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='CustomFieldChoice',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('value', models.CharField(max_length=100)),
+                ('weight', models.PositiveSmallIntegerField(default=100, help_text='Higher weights appear lower in the list')),
+                ('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='choices', to='extras.CustomField')),
+            ],
+            options={
+                'ordering': ['field', 'weight', 'value'],
+            },
+        ),
+        migrations.CreateModel(
+            name='CustomFieldValue',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('obj_id', models.PositiveIntegerField()),
+                ('serialized_value', models.CharField(max_length=255)),
+                ('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='extras.CustomField')),
+                ('obj_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
+            ],
+            options={
+                'ordering': ['obj_type', 'obj_id'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='customfieldvalue',
+            unique_together=set([('field', 'obj_type', 'obj_id')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='customfieldchoice',
+            unique_together=set([('field', 'value')]),
+        ),
+        migrations.CreateModel(
+            name='ImageAttachment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('object_id', models.PositiveIntegerField()),
+                ('image', models.ImageField(height_field='image_height', upload_to=extras.models.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'],
+            },
+        ),
+        migrations.RunPython(
+            code=verify_postgresql_version,
+        ),
+        migrations.CreateModel(
+            name='ReportResult',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('report', models.CharField(max_length=255, unique=True)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('failed', models.BooleanField()),
+                ('data', django.contrib.postgres.fields.jsonb.JSONField()),
+                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['report'],
+            },
+        ),
+    ]

+ 240 - 0
netbox/ipam/migrations/0002_vrf_add_enforce_unique_squashed_0018_remove_service_uniqueness_constraint.py

@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:12
+from __future__ import unicode_literals
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+import ipam.fields
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('ipam', '0002_vrf_add_enforce_unique'), ('ipam', '0003_ipam_add_vlangroups'), ('ipam', '0004_ipam_vlangroup_uniqueness'), ('ipam', '0005_auto_20160725_1842'), ('ipam', '0006_vrf_vlan_add_tenant'), ('ipam', '0007_prefix_ipaddress_add_tenant'), ('ipam', '0008_prefix_change_order'), ('ipam', '0009_ipaddress_add_status'), ('ipam', '0010_ipaddress_help_texts'), ('ipam', '0011_rir_add_is_private'), ('ipam', '0012_services'), ('ipam', '0013_prefix_add_is_pool'), ('ipam', '0014_ipaddress_status_add_deprecated'), ('ipam', '0015_global_vlans'), ('ipam', '0016_unicode_literals'), ('ipam', '0017_ipaddress_roles'), ('ipam', '0018_remove_service_uniqueness_constraint')]
+
+    dependencies = [
+        ('dcim', '0010_devicebay_installed_device_set_null'),
+        ('dcim', '0022_color_names_to_rgb'),
+        ('tenancy', '0001_initial'),
+        ('ipam', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='vrf',
+            name='enforce_unique',
+            field=models.BooleanField(default=True, help_text='Prevent duplicate prefixes/IP addresses within this VRF', verbose_name='Enforce unique space'),
+        ),
+        migrations.CreateModel(
+            name='VLANGroup',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50)),
+                ('slug', models.SlugField()),
+                ('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlan_groups', to='dcim.Site')),
+            ],
+            options={
+                'ordering': ['site', 'name'],
+            },
+        ),
+        migrations.AddField(
+            model_name='vlan',
+            name='group',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='ipam.VLANGroup'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='vlangroup',
+            unique_together=set([('site', 'slug'), ('site', 'name')]),
+        ),
+        migrations.AlterModelOptions(
+            name='vlan',
+            options={'ordering': ['site', 'group', 'vid'], 'verbose_name': 'VLAN', 'verbose_name_plural': 'VLANs'},
+        ),
+        migrations.AlterModelOptions(
+            name='vlangroup',
+            options={'ordering': ['site', 'name'], 'verbose_name': 'VLAN group', 'verbose_name_plural': 'VLAN groups'},
+        ),
+        migrations.AddField(
+            model_name='vlan',
+            name='description',
+            field=models.CharField(blank=True, max_length=100),
+        ),
+        migrations.AlterUniqueTogether(
+            name='vlan',
+            unique_together=set([('group', 'vid'), ('group', 'name')]),
+        ),
+        migrations.AlterField(
+            model_name='vlan',
+            name='name',
+            field=models.CharField(max_length=64),
+        ),
+        migrations.AddField(
+            model_name='vlan',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='tenancy.Tenant'),
+        ),
+        migrations.AddField(
+            model_name='vrf',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vrfs', to='tenancy.Tenant'),
+        ),
+        migrations.AddField(
+            model_name='ipaddress',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ip_addresses', to='tenancy.Tenant'),
+        ),
+        migrations.AddField(
+            model_name='prefix',
+            name='tenant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='tenancy.Tenant'),
+        ),
+        migrations.AlterModelOptions(
+            name='prefix',
+            options={'ordering': ['vrf', 'family', 'prefix'], 'verbose_name_plural': 'prefixes'},
+        ),
+        migrations.AddField(
+            model_name='ipaddress',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[(1, 'Active'), (2, 'Reserved'), (3, 'Deprecated'), (5, 'DHCP')], default=1, help_text='The operational status of this IP', verbose_name='Status'),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='address',
+            field=ipam.fields.IPAddressField(help_text=b'IPv4 or IPv6 address (with mask)'),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='nat_inside',
+            field=models.OneToOneField(blank=True, help_text=b'The IP for which this address is the "outside" IP', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='nat_outside', to='ipam.IPAddress', verbose_name=b'NAT (Inside)'),
+        ),
+        migrations.AddField(
+            model_name='rir',
+            name='is_private',
+            field=models.BooleanField(default=False, help_text='IP space managed by this RIR is considered private', verbose_name='Private'),
+        ),
+        migrations.CreateModel(
+            name='Service',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateField(auto_now_add=True)),
+                ('last_updated', models.DateTimeField(auto_now=True)),
+                ('name', models.CharField(max_length=30)),
+                ('protocol', models.PositiveSmallIntegerField(choices=[(6, 'TCP'), (17, 'UDP')])),
+                ('port', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65535)], verbose_name='Port number')),
+                ('description', models.CharField(blank=True, max_length=100)),
+                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='services', to='dcim.Device', verbose_name='device')),
+                ('ipaddresses', models.ManyToManyField(blank=True, related_name='services', to='ipam.IPAddress', verbose_name='IP addresses')),
+            ],
+            options={
+                'ordering': ['device', 'protocol', 'port'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='service',
+            unique_together=set([('device', 'protocol', 'port')]),
+        ),
+        migrations.AddField(
+            model_name='prefix',
+            name='is_pool',
+            field=models.BooleanField(default=False, help_text='All IP addresses within this prefix are considered usable', verbose_name='Is a pool'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='prefix',
+            field=ipam.fields.IPNetworkField(help_text=b'IPv4 or IPv6 network with mask'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='role',
+            field=models.ForeignKey(blank=True, help_text=b'The primary function of this prefix', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='prefixes', to='ipam.Role'),
+        ),
+        migrations.AlterField(
+            model_name='vlan',
+            name='site',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlans', to='dcim.Site'),
+        ),
+        migrations.AlterField(
+            model_name='aggregate',
+            name='family',
+            field=models.PositiveSmallIntegerField(choices=[(4, 'IPv4'), (6, 'IPv6')]),
+        ),
+        migrations.AlterField(
+            model_name='aggregate',
+            name='rir',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='aggregates', to='ipam.RIR', verbose_name='RIR'),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='address',
+            field=ipam.fields.IPAddressField(help_text='IPv4 or IPv6 address (with mask)'),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='family',
+            field=models.PositiveSmallIntegerField(choices=[(4, 'IPv4'), (6, 'IPv6')], editable=False),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='nat_inside',
+            field=models.OneToOneField(blank=True, help_text='The IP for which this address is the "outside" IP', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='nat_outside', to='ipam.IPAddress', verbose_name='NAT (Inside)'),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='vrf',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ip_addresses', to='ipam.VRF', verbose_name='VRF'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='family',
+            field=models.PositiveSmallIntegerField(choices=[(4, 'IPv4'), (6, 'IPv6')], editable=False),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='prefix',
+            field=ipam.fields.IPNetworkField(help_text='IPv4 or IPv6 network with mask'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='role',
+            field=models.ForeignKey(blank=True, help_text='The primary function of this prefix', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='prefixes', to='ipam.Role'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[(0, 'Container'), (1, 'Active'), (2, 'Reserved'), (3, 'Deprecated')], default=1, help_text='Operational status of this prefix', verbose_name='Status'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='vlan',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='ipam.VLAN', verbose_name='VLAN'),
+        ),
+        migrations.AlterField(
+            model_name='prefix',
+            name='vrf',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='ipam.VRF', verbose_name='VRF'),
+        ),
+        migrations.AlterField(
+            model_name='vlan',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[(1, 'Active'), (2, 'Reserved'), (3, 'Deprecated')], default=1, verbose_name='Status'),
+        ),
+        migrations.AlterField(
+            model_name='vlan',
+            name='vid',
+            field=models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(4094)], verbose_name='ID'),
+        ),
+        migrations.AlterField(
+            model_name='vrf',
+            name='rd',
+            field=models.CharField(max_length=21, unique=True, verbose_name='Route distinguisher'),
+        ),
+        migrations.AddField(
+            model_name='ipaddress',
+            name='role',
+            field=models.PositiveSmallIntegerField(blank=True, choices=[(10, 'Loopback'), (20, 'Secondary'), (30, 'Anycast'), (40, 'VIP'), (41, 'VRRP'), (42, 'HSRP'), (43, 'GLBP')], help_text='The functional role of this IP', null=True, verbose_name='Role'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='service',
+            unique_together=set([]),
+        ),
+    ]

+ 38 - 0
netbox/ipam/migrations/0019_virtualization_squashed_0020_ipaddress_add_role_carp.py

@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:14
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('ipam', '0019_virtualization'), ('ipam', '0020_ipaddress_add_role_carp')]
+
+    dependencies = [
+        ('ipam', '0018_remove_service_uniqueness_constraint'),
+        ('virtualization', '0001_virtualization'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='service',
+            options={'ordering': ['protocol', 'port']},
+        ),
+        migrations.AddField(
+            model_name='service',
+            name='virtual_machine',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='services', to='virtualization.VirtualMachine'),
+        ),
+        migrations.AlterField(
+            model_name='service',
+            name='device',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='services', to='dcim.Device', verbose_name='device'),
+        ),
+        migrations.AlterField(
+            model_name='ipaddress',
+            name='role',
+            field=models.PositiveSmallIntegerField(blank=True, choices=[(10, 'Loopback'), (20, 'Secondary'), (30, 'Anycast'), (40, 'VIP'), (41, 'VRRP'), (42, 'HSRP'), (43, 'GLBP'), (44, 'CARP')], help_text='The functional role of this IP', null=True, verbose_name='Role'),
+        ),
+    ]

+ 82 - 0
netbox/secrets/migrations/0001_initial_squashed_0003_unicode_literals.py

@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-08-01 17:45
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('secrets', '0001_initial'), ('secrets', '0002_userkey_add_session_key'), ('secrets', '0003_unicode_literals')]
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('dcim', '0002_auto_20160622_1821'),
+        ('auth', '0007_alter_validators_add_error_messages'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='SecretRole',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=50, unique=True)),
+                ('slug', models.SlugField(unique=True)),
+                ('groups', models.ManyToManyField(blank=True, related_name='secretroles', to='auth.Group')),
+                ('users', models.ManyToManyField(blank=True, related_name='secretroles', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'ordering': ['name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='Secret',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateField(auto_now_add=True)),
+                ('last_updated', models.DateTimeField(auto_now=True)),
+                ('name', models.CharField(blank=True, max_length=100)),
+                ('ciphertext', models.BinaryField(max_length=65568)),
+                ('hash', models.CharField(editable=False, max_length=128)),
+                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='secrets', to='dcim.Device')),
+                ('role', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='secrets', to='secrets.SecretRole')),
+            ],
+            options={
+                'ordering': ['device', 'role', 'name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='UserKey',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateField(auto_now_add=True)),
+                ('last_updated', models.DateTimeField(auto_now=True)),
+                ('public_key', models.TextField(verbose_name='RSA public key')),
+                ('master_key_cipher', models.BinaryField(blank=True, max_length=512, null=True)),
+                ('user', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='user_key', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'permissions': (('activate_userkey', 'Can activate user keys for decryption'),),
+                'ordering': ['user__username'],
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='secret',
+            unique_together=set([('device', 'role', 'name')]),
+        ),
+        migrations.CreateModel(
+            name='SessionKey',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('cipher', models.BinaryField(max_length=512)),
+                ('hash', models.CharField(editable=False, max_length=128)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('userkey', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='session_key', to='secrets.UserKey')),
+            ],
+            options={
+                'ordering': ['userkey__user__username'],
+            },
+        ),
+    ]

+ 28 - 0
netbox/tenancy/migrations/0002_tenant_group_optional_squashed_0003_unicode_literals.py

@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:12
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('tenancy', '0002_tenant_group_optional'), ('tenancy', '0003_unicode_literals')]
+
+    dependencies = [
+        ('tenancy', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='tenant',
+            name='group',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tenants', to='tenancy.TenantGroup'),
+        ),
+        migrations.AlterField(
+            model_name='tenant',
+            name='description',
+            field=models.CharField(blank=True, help_text='Long-form name (optional)', max_length=100),
+        ),
+    ]

+ 35 - 0
netbox/users/migrations/0001_api_tokens_squashed_0002_unicode_literals.py

@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-08-01 17:43
+from __future__ import unicode_literals
+
+from django.conf import settings
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('users', '0001_api_tokens'), ('users', '0002_unicode_literals')]
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Token',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('expires', models.DateTimeField(blank=True, null=True)),
+                ('key', models.CharField(max_length=40, unique=True, validators=[django.core.validators.MinLengthValidator(40)])),
+                ('write_enabled', models.BooleanField(default=True, help_text='Permit create/update/delete operations using this key')),
+                ('description', models.CharField(blank=True, max_length=100)),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'default_permissions': [],
+            },
+        ),
+    ]

+ 34 - 0
netbox/virtualization/migrations/0002_virtualmachine_add_status_squashed_0004_virtualmachine_add_role.py

@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.14 on 2018-07-31 02:23
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('virtualization', '0002_virtualmachine_add_status'), ('virtualization', '0003_cluster_add_site'), ('virtualization', '0004_virtualmachine_add_role')]
+
+    dependencies = [
+        ('dcim', '0044_virtualization'),
+        ('virtualization', '0001_virtualization'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='virtualmachine',
+            name='status',
+            field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [0, 'Offline'], [3, 'Staged']], default=1, verbose_name='Status'),
+        ),
+        migrations.AddField(
+            model_name='cluster',
+            name='site',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='clusters', to='dcim.Site'),
+        ),
+        migrations.AddField(
+            model_name='virtualmachine',
+            name='role',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='dcim.DeviceRole'),
+        ),
+    ]