From 1a5cb5a9f9feac652bcea62668948d18c1af237d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 28 May 2025 13:25:06 -0400 Subject: [PATCH 1/7] Initial work on ObjectChange data migrations --- .../0047_circuittermination__termination.py | 26 +++++++++++++++++++ .../0051_virtualcircuit_group_assignment.py | 19 ++++++++++++++ netbox/ipam/migrations/0071_prefix_scope.py | 19 ++++++++++++++ .../0080_populate_service_parent.py | 26 +++++++++++++++++++ .../tenancy/migrations/0018_contact_groups.py | 14 ++++++++++ .../migrations/0044_cluster_scope.py | 19 ++++++++++++++ 6 files changed, 123 insertions(+) diff --git a/netbox/circuits/migrations/0047_circuittermination__termination.py b/netbox/circuits/migrations/0047_circuittermination__termination.py index af3af157b3b..546450ee7be 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,28 @@ 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, revert): + 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, + }) + data.pop('site') + data.pop('provider_network') + + +objectchange_migrators = { + 'circuits.circuittermination': oc_circuittermination_termination, +} diff --git a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py index 0a694465aaf..448a1e6252c 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, revert): + 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') + + +objectchange_migrators = { + 'circuits.circuitgroupassignment': oc_circuitgroupassignment_member, +} diff --git a/netbox/ipam/migrations/0071_prefix_scope.py b/netbox/ipam/migrations/0071_prefix_scope.py index 02047dc0925..d7c2c2fcd3a 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,21 @@ 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, revert): + 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, + }) + data.pop('site') + + +objectchange_migrators = { + 'ipam.prefix': oc_prefix_scope, +} diff --git a/netbox/ipam/migrations/0080_populate_service_parent.py b/netbox/ipam/migrations/0080_populate_service_parent.py index f043926bc58..62f13df1881 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,28 @@ class Migration(migrations.Migration): reverse_code=repopulate_device_and_virtualmachine_relations, ) ] + + +def oc_service_parent(objectchange, revert): + 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, + }) + data.pop('device') + data.pop('virtual_machine') + + +objectchange_migrators = { + 'ipam.service': oc_service_parent, +} diff --git a/netbox/tenancy/migrations/0018_contact_groups.py b/netbox/tenancy/migrations/0018_contact_groups.py index 1279ccdf727..72d2d95055f 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, revert): + for data in (objectchange.prechange_data, objectchange.postchange_data): + if data is None: + continue + if 'group' in data: + data['groups'] = [data['group']] if data['group'] else [] + data.pop('group') + + +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 532bc79c132..03d6481ff61 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,21 @@ 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, revert): + 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, + }) + data.pop('site') + + +objectchange_migrators = { + 'virtualization.cluster': oc_cluster_scope, +} From ef0a68641739cd5f96e8ac3d368a89a3f1406d76 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 28 May 2025 15:37:55 -0400 Subject: [PATCH 2/7] Fix migration bug --- netbox/dcim/migrations/0200_populate_mac_addresses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/migrations/0200_populate_mac_addresses.py b/netbox/dcim/migrations/0200_populate_mac_addresses.py index 8536d7d79a3..6ffb7173115 100644 --- a/netbox/dcim/migrations/0200_populate_mac_addresses.py +++ b/netbox/dcim/migrations/0200_populate_mac_addresses.py @@ -15,7 +15,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) From 354c5dc1c5757bf8d532f447ca7f2770e75bafb4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 29 May 2025 09:37:07 -0400 Subject: [PATCH 3/7] Add migrators for MAC address assignments --- .../migrations/0200_populate_mac_addresses.py | 40 +++++++++++++++++++ .../migrations/0048_populate_mac_addresses.py | 40 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/netbox/dcim/migrations/0200_populate_mac_addresses.py b/netbox/dcim/migrations/0200_populate_mac_addresses.py index 6ffb7173115..48d2c746bee 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 @@ -51,3 +53,41 @@ class Migration(migrations.Migration): name='mac_address', ), ] + + +def oc_interface_primary_mac_address(objectchange, revert): + MACAddress = apps.get_model('dcim', 'MACAddress') + interface_ct = ContentType.objects.get_by_natural_key('dcim', 'interface') + + if not revert: + 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') + after.pop('mac_address') + + +objectchange_migrators = { + 'dcim.interface': oc_interface_primary_mac_address, +} diff --git a/netbox/virtualization/migrations/0048_populate_mac_addresses.py b/netbox/virtualization/migrations/0048_populate_mac_addresses.py index fe485d52e72..6de748fe880 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,41 @@ class Migration(migrations.Migration): name='mac_address', ), ] + + +def oc_vminterface_primary_mac_address(objectchange, revert): + MACAddress = apps.get_model('dcim', 'MACAddress') + vminterface_ct = ContentType.objects.get_by_natural_key('virtualization', 'vminterface') + + if not revert: + 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') + after.pop('mac_address') + + +objectchange_migrators = { + 'virtualization.vminterface': oc_vminterface_primary_mac_address, +} From 5a0d33a4a819590cfc12104fe09061b471f03df6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 2 Jun 2025 09:45:44 -0400 Subject: [PATCH 4/7] Update reverting kwarg; allow pop() to fail --- .../migrations/0047_circuittermination__termination.py | 6 +++--- .../migrations/0051_virtualcircuit_group_assignment.py | 4 ++-- netbox/dcim/migrations/0200_populate_mac_addresses.py | 9 +++++---- netbox/ipam/migrations/0071_prefix_scope.py | 4 ++-- netbox/ipam/migrations/0080_populate_service_parent.py | 6 +++--- netbox/tenancy/migrations/0018_contact_groups.py | 8 ++++---- netbox/virtualization/migrations/0044_cluster_scope.py | 4 ++-- .../migrations/0048_populate_mac_addresses.py | 9 +++++---- 8 files changed, 26 insertions(+), 24 deletions(-) diff --git a/netbox/circuits/migrations/0047_circuittermination__termination.py b/netbox/circuits/migrations/0047_circuittermination__termination.py index 546450ee7be..6a6d0850292 100644 --- a/netbox/circuits/migrations/0047_circuittermination__termination.py +++ b/netbox/circuits/migrations/0047_circuittermination__termination.py @@ -52,7 +52,7 @@ class Migration(migrations.Migration): ] -def oc_circuittermination_termination(objectchange, revert): +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): @@ -68,8 +68,8 @@ def oc_circuittermination_termination(objectchange, revert): 'termination_type': provider_network_ct, 'termination_id': provider_network_id, }) - data.pop('site') - data.pop('provider_network') + data.pop('site', None) + data.pop('provider_network', None) objectchange_migrators = { diff --git a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py index 448a1e6252c..50f5fd5a691 100644 --- a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py +++ b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py @@ -85,7 +85,7 @@ class Migration(migrations.Migration): ] -def oc_circuitgroupassignment_member(objectchange, revert): +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: @@ -95,7 +95,7 @@ def oc_circuitgroupassignment_member(objectchange, revert): 'member_type': circuit_ct, 'member_id': circuit_id, }) - data.pop('circuit') + data.pop('circuit', None) objectchange_migrators = { diff --git a/netbox/dcim/migrations/0200_populate_mac_addresses.py b/netbox/dcim/migrations/0200_populate_mac_addresses.py index 48d2c746bee..1b548b29c6a 100644 --- a/netbox/dcim/migrations/0200_populate_mac_addresses.py +++ b/netbox/dcim/migrations/0200_populate_mac_addresses.py @@ -55,11 +55,12 @@ class Migration(migrations.Migration): ] -def oc_interface_primary_mac_address(objectchange, revert): +def oc_interface_primary_mac_address(objectchange, reverting): MACAddress = apps.get_model('dcim', 'MACAddress') interface_ct = ContentType.objects.get_by_natural_key('dcim', 'interface') - if not revert: + # 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 @@ -84,8 +85,8 @@ def oc_interface_primary_mac_address(objectchange, revert): ).delete() before['primary_mac_address'] = None - before.pop('mac_address') - after.pop('mac_address') + before.pop('mac_address', None) + after.pop('mac_address', None) objectchange_migrators = { diff --git a/netbox/ipam/migrations/0071_prefix_scope.py b/netbox/ipam/migrations/0071_prefix_scope.py index d7c2c2fcd3a..af3659303ff 100644 --- a/netbox/ipam/migrations/0071_prefix_scope.py +++ b/netbox/ipam/migrations/0071_prefix_scope.py @@ -47,7 +47,7 @@ class Migration(migrations.Migration): ] -def oc_prefix_scope(objectchange, revert): +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: @@ -57,7 +57,7 @@ def oc_prefix_scope(objectchange, revert): 'scope_type': site_ct, 'scope_id': site_id, }) - data.pop('site') + data.pop('site', None) objectchange_migrators = { diff --git a/netbox/ipam/migrations/0080_populate_service_parent.py b/netbox/ipam/migrations/0080_populate_service_parent.py index 62f13df1881..df4e03af7df 100644 --- a/netbox/ipam/migrations/0080_populate_service_parent.py +++ b/netbox/ipam/migrations/0080_populate_service_parent.py @@ -57,7 +57,7 @@ class Migration(migrations.Migration): ] -def oc_service_parent(objectchange, revert): +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): @@ -73,8 +73,8 @@ def oc_service_parent(objectchange, revert): 'parent_object_type': virtual_machine_ct, 'parent_object_id': virtual_machine_id, }) - data.pop('device') - data.pop('virtual_machine') + data.pop('device', None) + data.pop('virtual_machine', None) objectchange_migrators = { diff --git a/netbox/tenancy/migrations/0018_contact_groups.py b/netbox/tenancy/migrations/0018_contact_groups.py index 72d2d95055f..7f21190f586 100644 --- a/netbox/tenancy/migrations/0018_contact_groups.py +++ b/netbox/tenancy/migrations/0018_contact_groups.py @@ -68,13 +68,13 @@ class Migration(migrations.Migration): ] -def oc_contact_groups(objectchange, revert): +def oc_contact_groups(objectchange, reverting): for data in (objectchange.prechange_data, objectchange.postchange_data): if data is None: continue - if 'group' in data: - data['groups'] = [data['group']] if data['group'] else [] - data.pop('group') + # 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 = { diff --git a/netbox/virtualization/migrations/0044_cluster_scope.py b/netbox/virtualization/migrations/0044_cluster_scope.py index 03d6481ff61..7664d34f07d 100644 --- a/netbox/virtualization/migrations/0044_cluster_scope.py +++ b/netbox/virtualization/migrations/0044_cluster_scope.py @@ -46,7 +46,7 @@ class Migration(migrations.Migration): ] -def oc_cluster_scope(objectchange, revert): +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: @@ -56,7 +56,7 @@ def oc_cluster_scope(objectchange, revert): 'scope_type': site_ct, 'scope_id': site_id, }) - data.pop('site') + data.pop('site', None) objectchange_migrators = { diff --git a/netbox/virtualization/migrations/0048_populate_mac_addresses.py b/netbox/virtualization/migrations/0048_populate_mac_addresses.py index 6de748fe880..c3aefdc68ab 100644 --- a/netbox/virtualization/migrations/0048_populate_mac_addresses.py +++ b/netbox/virtualization/migrations/0048_populate_mac_addresses.py @@ -54,11 +54,12 @@ class Migration(migrations.Migration): ] -def oc_vminterface_primary_mac_address(objectchange, revert): +def oc_vminterface_primary_mac_address(objectchange, reverting): MACAddress = apps.get_model('dcim', 'MACAddress') vminterface_ct = ContentType.objects.get_by_natural_key('virtualization', 'vminterface') - if not revert: + # 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 @@ -83,8 +84,8 @@ def oc_vminterface_primary_mac_address(objectchange, revert): ).delete() before['primary_mac_address'] = None - before.pop('mac_address') - after.pop('mac_address') + before.pop('mac_address', None) + after.pop('mac_address', None) objectchange_migrators = { From 71ee2a996e153507973e38398a18c4bbfaa9755d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 3 Jun 2025 15:15:30 -0400 Subject: [PATCH 5/7] Cross-reference MAC address migrators --- netbox/dcim/migrations/0200_populate_mac_addresses.py | 1 + netbox/virtualization/migrations/0048_populate_mac_addresses.py | 1 + 2 files changed, 2 insertions(+) diff --git a/netbox/dcim/migrations/0200_populate_mac_addresses.py b/netbox/dcim/migrations/0200_populate_mac_addresses.py index 1b548b29c6a..7c44a2cca38 100644 --- a/netbox/dcim/migrations/0200_populate_mac_addresses.py +++ b/netbox/dcim/migrations/0200_populate_mac_addresses.py @@ -55,6 +55,7 @@ class Migration(migrations.Migration): ] +# 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') diff --git a/netbox/virtualization/migrations/0048_populate_mac_addresses.py b/netbox/virtualization/migrations/0048_populate_mac_addresses.py index c3aefdc68ab..45ba8fa1256 100644 --- a/netbox/virtualization/migrations/0048_populate_mac_addresses.py +++ b/netbox/virtualization/migrations/0048_populate_mac_addresses.py @@ -54,6 +54,7 @@ class Migration(migrations.Migration): ] +# 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') From d223c01dcdd0dc44275695a4b9d8ea29ee0f27fa Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 3 Jun 2025 15:32:17 -0400 Subject: [PATCH 6/7] Split migrator logic across migrations --- .../0047_circuittermination__termination.py | 2 -- .../0048_circuitterminations_cached_relations.py | 12 ++++++++++++ netbox/ipam/migrations/0071_prefix_scope.py | 1 - .../ipam/migrations/0072_prefix_cached_relations.py | 11 +++++++++++ .../ipam/migrations/0080_populate_service_parent.py | 2 -- ...ce_device_virtual_machine_add_parent_gfk_index.py | 12 ++++++++++++ .../virtualization/migrations/0044_cluster_scope.py | 1 - .../migrations/0045_clusters_cached_relations.py | 11 +++++++++++ 8 files changed, 46 insertions(+), 6 deletions(-) diff --git a/netbox/circuits/migrations/0047_circuittermination__termination.py b/netbox/circuits/migrations/0047_circuittermination__termination.py index 6a6d0850292..0b0c6233cd1 100644 --- a/netbox/circuits/migrations/0047_circuittermination__termination.py +++ b/netbox/circuits/migrations/0047_circuittermination__termination.py @@ -68,8 +68,6 @@ def oc_circuittermination_termination(objectchange, reverting): 'termination_type': provider_network_ct, 'termination_id': provider_network_id, }) - data.pop('site', None) - data.pop('provider_network', None) objectchange_migrators = { diff --git a/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py b/netbox/circuits/migrations/0048_circuitterminations_cached_relations.py index cd228b6616f..f2676c2ee07 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/ipam/migrations/0071_prefix_scope.py b/netbox/ipam/migrations/0071_prefix_scope.py index af3659303ff..bf80f9b5e1c 100644 --- a/netbox/ipam/migrations/0071_prefix_scope.py +++ b/netbox/ipam/migrations/0071_prefix_scope.py @@ -57,7 +57,6 @@ def oc_prefix_scope(objectchange, reverting): 'scope_type': site_ct, 'scope_id': site_id, }) - data.pop('site', None) objectchange_migrators = { diff --git a/netbox/ipam/migrations/0072_prefix_cached_relations.py b/netbox/ipam/migrations/0072_prefix_cached_relations.py index c46f5b1a47c..1dcc55a7490 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 df4e03af7df..bd9d1c8b5c0 100644 --- a/netbox/ipam/migrations/0080_populate_service_parent.py +++ b/netbox/ipam/migrations/0080_populate_service_parent.py @@ -73,8 +73,6 @@ def oc_service_parent(objectchange, reverting): 'parent_object_type': virtual_machine_ct, 'parent_object_id': virtual_machine_id, }) - data.pop('device', None) - data.pop('virtual_machine', None) objectchange_migrators = { 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 03b63cd1225..f026fc65449 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/virtualization/migrations/0044_cluster_scope.py b/netbox/virtualization/migrations/0044_cluster_scope.py index 7664d34f07d..da5a94ac3f4 100644 --- a/netbox/virtualization/migrations/0044_cluster_scope.py +++ b/netbox/virtualization/migrations/0044_cluster_scope.py @@ -56,7 +56,6 @@ def oc_cluster_scope(objectchange, reverting): 'scope_type': site_ct, 'scope_id': site_id, }) - data.pop('site', None) objectchange_migrators = { diff --git a/netbox/virtualization/migrations/0045_clusters_cached_relations.py b/netbox/virtualization/migrations/0045_clusters_cached_relations.py index 71a6129b789..c741be4fad3 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, +} From b51a3a1710e7a7db78cdd5b6a993a4aea79e75d7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 3 Jun 2025 15:43:50 -0400 Subject: [PATCH 7/7] Add missing migrator --- netbox/dcim/migrations/0188_racktype.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/netbox/dcim/migrations/0188_racktype.py b/netbox/dcim/migrations/0188_racktype.py index a5265d030ac..7c36e03ba27 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, +}