diff --git a/netbox/circuits/migrations/0047_circuittermination__termination.py b/netbox/circuits/migrations/0047_circuittermination__termination.py index af3af157b..0b0c6233c 100644 --- a/netbox/circuits/migrations/0047_circuittermination__termination.py +++ b/netbox/circuits/migrations/0047_circuittermination__termination.py @@ -1,4 +1,5 @@ import django.db.models.deletion +from django.contrib.contenttypes.models import ContentType from django.db import migrations, models @@ -49,3 +50,26 @@ class Migration(migrations.Migration): # Copy over existing site assignments migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop), ] + + +def oc_circuittermination_termination(objectchange, reverting): + site_ct = ContentType.objects.get_by_natural_key('dcim', 'site').pk + provider_network_ct = ContentType.objects.get_by_natural_key('circuits', 'providernetwork').pk + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if site_id := data.get('site'): + data.update({ + 'termination_type': site_ct, + 'termination_id': site_id, + }) + elif provider_network_id := data.get('provider_network'): + data.update({ + 'termination_type': provider_network_ct, + 'termination_id': provider_network_id, + }) + + +objectchange_migrators = { + 'circuits.circuittermination': oc_circuittermination_termination, +} diff --git a/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py b/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py index cd228b661..f2676c2ee 100644 --- a/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py +++ b/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py @@ -86,3 +86,15 @@ class Migration(migrations.Migration): new_name='_provider_network', ), ] + + +def oc_circuittermination_remove_fields(objectchange, reverting): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is not None: + data.pop('site', None) + data.pop('provider_network', None) + + +objectchange_migrators = { + 'circuits.circuittermination': oc_circuittermination_remove_fields, +} diff --git a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py index 0a694465a..50f5fd5a6 100644 --- a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py +++ b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py @@ -1,4 +1,5 @@ import django.db.models.deletion +from django.contrib.contenttypes.models import ContentType from django.db import migrations, models @@ -82,3 +83,21 @@ class Migration(migrations.Migration): ), ), ] + + +def oc_circuitgroupassignment_member(objectchange, reverting): + circuit_ct = ContentType.objects.get_by_natural_key('circuits', 'circuit').pk + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if circuit_id := data.get('circuit'): + data.update({ + 'member_type': circuit_ct, + 'member_id': circuit_id, + }) + data.pop('circuit', None) + + +objectchange_migrators = { + 'circuits.circuitgroupassignment': oc_circuitgroupassignment_member, +} diff --git a/netbox/dcim/migrations/0188_racktype.py b/netbox/dcim/migrations/0188_racktype.py index a5265d030..7c36e03ba 100644 --- a/netbox/dcim/migrations/0188_racktype.py +++ b/netbox/dcim/migrations/0188_racktype.py @@ -100,3 +100,16 @@ class Migration(migrations.Migration): ), ), ] + + +def oc_rename_type(objectchange, reverting): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if 'type' in data: + data['form_factor'] = data.pop('type') + + +objectchange_migrators = { + 'dcim.rack': oc_rename_type, +} diff --git a/netbox/dcim/migrations/0200_populate_mac_addresses.py b/netbox/dcim/migrations/0200_populate_mac_addresses.py index 8536d7d79..7c44a2cca 100644 --- a/netbox/dcim/migrations/0200_populate_mac_addresses.py +++ b/netbox/dcim/migrations/0200_populate_mac_addresses.py @@ -1,4 +1,6 @@ import django.db.models.deletion +from django.apps import apps +from django.contrib.contenttypes.models import ContentType from django.db import migrations, models @@ -15,7 +17,7 @@ def populate_mac_addresses(apps, schema_editor): assigned_object_type=interface_ct, assigned_object_id=interface.pk ) - for interface in Interface.objects.filter(mac_address__isnull=False) + for interface in Interface.objects.using(db_alias).filter(mac_address__isnull=False) ] MACAddress.objects.using(db_alias).bulk_create(mac_addresses, batch_size=100) @@ -51,3 +53,43 @@ class Migration(migrations.Migration): name='mac_address', ), ] + + +# See peer migrator in virtualization.0048_populate_mac_addresses before making changes +def oc_interface_primary_mac_address(objectchange, reverting): + MACAddress = apps.get_model('dcim', 'MACAddress') + interface_ct = ContentType.objects.get_by_natural_key('dcim', 'interface') + + # Swap data order if the change is being reverted + if not reverting: + before, after = objectchange.prechange_data, objectchange.postchange_data + else: + before, after = objectchange.postchange_data, objectchange.prechange_data + + if after.get('mac_address') != before.get('mac_address'): + # Create & assign the new MACAddress (if any) + if after.get('mac_address'): + mac = MACAddress.objects.create( + mac_address=after['mac_address'], + assigned_object_type=interface_ct, + assigned_object_id=objectchange.changed_object_id, + ) + after['primary_mac_address'] = mac.pk + else: + after['primary_mac_address'] = None + # Delete the old MACAddress (if any) + if before.get('mac_address'): + MACAddress.objects.filter( + mac_address=before['mac_address'], + assigned_object_type=interface_ct, + assigned_object_id=objectchange.changed_object_id, + ).delete() + before['primary_mac_address'] = None + + before.pop('mac_address', None) + after.pop('mac_address', None) + + +objectchange_migrators = { + 'dcim.interface': oc_interface_primary_mac_address, +} diff --git a/netbox/ipam/migrations/0071_prefix_scope.py b/netbox/ipam/migrations/0071_prefix_scope.py index 02047dc09..bf80f9b5e 100644 --- a/netbox/ipam/migrations/0071_prefix_scope.py +++ b/netbox/ipam/migrations/0071_prefix_scope.py @@ -1,4 +1,5 @@ import django.db.models.deletion +from django.contrib.contenttypes.models import ContentType from django.db import migrations, models @@ -44,3 +45,20 @@ class Migration(migrations.Migration): # Copy over existing site assignments migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop), ] + + +def oc_prefix_scope(objectchange, reverting): + site_ct = ContentType.objects.get_by_natural_key('dcim', 'site').pk + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if site_id := data.get('site'): + data.update({ + 'scope_type': site_ct, + 'scope_id': site_id, + }) + + +objectchange_migrators = { + 'ipam.prefix': oc_prefix_scope, +} diff --git a/netbox/ipam/migrations/0072_prefix_cached_relations.py b/netbox/ipam/migrations/0072_prefix_cached_relations.py index c46f5b1a4..1dcc55a74 100644 --- a/netbox/ipam/migrations/0072_prefix_cached_relations.py +++ b/netbox/ipam/migrations/0072_prefix_cached_relations.py @@ -60,3 +60,14 @@ class Migration(migrations.Migration): name='site', ), ] + + +def oc_prefix_remove_fields(objectchange, reverting): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is not None: + data.pop('site', None) + + +objectchange_migrators = { + 'ipam.prefix': oc_prefix_remove_fields, +} diff --git a/netbox/ipam/migrations/0080_populate_service_parent.py b/netbox/ipam/migrations/0080_populate_service_parent.py index f043926bc..bd9d1c8b5 100644 --- a/netbox/ipam/migrations/0080_populate_service_parent.py +++ b/netbox/ipam/migrations/0080_populate_service_parent.py @@ -1,3 +1,4 @@ +from django.contrib.contenttypes.models import ContentType from django.db import migrations from django.db.models import F @@ -54,3 +55,26 @@ class Migration(migrations.Migration): reverse_code=repopulate_device_and_virtualmachine_relations, ) ] + + +def oc_service_parent(objectchange, reverting): + device_ct = ContentType.objects.get_by_natural_key('dcim', 'device').pk + virtual_machine_ct = ContentType.objects.get_by_natural_key('virtualization', 'virtualmachine').pk + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if device_id := data.get('device'): + data.update({ + 'parent_object_type': device_ct, + 'parent_object_id': device_id, + }) + elif virtual_machine_id := data.get('virtual_machine'): + data.update({ + 'parent_object_type': virtual_machine_ct, + 'parent_object_id': virtual_machine_id, + }) + + +objectchange_migrators = { + 'ipam.service': oc_service_parent, +} diff --git a/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py b/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py index 03b63cd12..f026fc654 100644 --- a/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py +++ b/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py @@ -37,3 +37,15 @@ class Migration(migrations.Migration): ), ), ] + + +def oc_service_remove_fields(objectchange, reverting): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is not None: + data.pop('device', None) + data.pop('virtual_machine', None) + + +objectchange_migrators = { + 'ipam.service': oc_service_remove_fields, +} diff --git a/netbox/tenancy/migrations/0018_contact_groups.py b/netbox/tenancy/migrations/0018_contact_groups.py index 1279ccdf7..7f21190f5 100644 --- a/netbox/tenancy/migrations/0018_contact_groups.py +++ b/netbox/tenancy/migrations/0018_contact_groups.py @@ -66,3 +66,17 @@ class Migration(migrations.Migration): name='group', ), ] + + +def oc_contact_groups(objectchange, reverting): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + # Set the M2M field `groups` to a list containing the group ID + data['groups'] = [data['group']] if data.get('group') else [] + data.pop('group', None) + + +objectchange_migrators = { + 'tenancy.contact': oc_contact_groups, +} diff --git a/netbox/virtualization/migrations/0044_cluster_scope.py b/netbox/virtualization/migrations/0044_cluster_scope.py index 532bc79c1..da5a94ac3 100644 --- a/netbox/virtualization/migrations/0044_cluster_scope.py +++ b/netbox/virtualization/migrations/0044_cluster_scope.py @@ -1,4 +1,5 @@ import django.db.models.deletion +from django.contrib.contenttypes.models import ContentType from django.db import migrations, models @@ -43,3 +44,20 @@ class Migration(migrations.Migration): # Copy over existing site assignments migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop), ] + + +def oc_cluster_scope(objectchange, reverting): + site_ct = ContentType.objects.get_by_natural_key('dcim', 'site').pk + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if site_id := data.get('site'): + data.update({ + 'scope_type': site_ct, + 'scope_id': site_id, + }) + + +objectchange_migrators = { + 'virtualization.cluster': oc_cluster_scope, +} diff --git a/netbox/virtualization/migrations/0045_clusters_cached_relations.py b/netbox/virtualization/migrations/0045_clusters_cached_relations.py index 71a6129b7..c741be4fa 100644 --- a/netbox/virtualization/migrations/0045_clusters_cached_relations.py +++ b/netbox/virtualization/migrations/0045_clusters_cached_relations.py @@ -87,3 +87,14 @@ class Migration(migrations.Migration): ), ), ] + + +def oc_cluster_remove_site(objectchange, reverting): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is not None: + data.pop('site', None) + + +objectchange_migrators = { + 'virtualization.cluster': oc_cluster_remove_site, +} diff --git a/netbox/virtualization/migrations/0048_populate_mac_addresses.py b/netbox/virtualization/migrations/0048_populate_mac_addresses.py index fe485d52e..45ba8fa12 100644 --- a/netbox/virtualization/migrations/0048_populate_mac_addresses.py +++ b/netbox/virtualization/migrations/0048_populate_mac_addresses.py @@ -1,4 +1,6 @@ import django.db.models.deletion +from django.apps import apps +from django.contrib.contenttypes.models import ContentType from django.db import migrations, models @@ -50,3 +52,43 @@ class Migration(migrations.Migration): name='mac_address', ), ] + + +# See peer migrator in dcim.0200_populate_mac_addresses before making changes +def oc_vminterface_primary_mac_address(objectchange, reverting): + MACAddress = apps.get_model('dcim', 'MACAddress') + vminterface_ct = ContentType.objects.get_by_natural_key('virtualization', 'vminterface') + + # Swap data order if the change is being reverted + if not reverting: + before, after = objectchange.prechange_data, objectchange.postchange_data + else: + before, after = objectchange.postchange_data, objectchange.prechange_data + + if after.get('mac_address') != before.get('mac_address'): + # Create & assign the new MACAddress (if any) + if after.get('mac_address'): + mac = MACAddress.objects.create( + mac_address=after['mac_address'], + assigned_object_type=vminterface_ct, + assigned_object_id=objectchange.changed_object_id, + ) + after['primary_mac_address'] = mac.pk + else: + after['primary_mac_address'] = None + # Delete the old MACAddress (if any) + if before.get('mac_address'): + MACAddress.objects.filter( + mac_address=before['mac_address'], + assigned_object_type=vminterface_ct, + assigned_object_id=objectchange.changed_object_id, + ).delete() + before['primary_mac_address'] = None + + before.pop('mac_address', None) + after.pop('mac_address', None) + + +objectchange_migrators = { + 'virtualization.vminterface': oc_vminterface_primary_mac_address, +}