Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions coriolis/schemas/vm_export_info_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@
"type": "string",
"description": "The allocation scheme for the given disk (static = thick; dynamic = thin)",
"enum": ["static", "dynamic"]
},
"legacy_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Identifiers which previous versions of the source provider may have reported as this disk's 'id' (e.g. the VMware provider moving from per-VM device keys to virtual disk UUIDs). Used to migrate the 'disk_id' fields of pre-existing replicas' volumes_info to the current identifier."
}
},
"required": [
Expand Down
60 changes: 60 additions & 0 deletions coriolis/tasks/replica_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,60 @@ def _check_ensure_volumes_info_ordering(export_info, volumes_info):
return ordered_volumes_info


def _update_legacy_disk_ids_in_volumes_info(export_info, volumes_info):
"""Migrates volumes_info entries keyed on legacy disk identifiers.

Source providers may change their disk identification methodology
over time (e.g. the VMware provider moving from per-VM device keys
to virtual disk UUIDs). Replicas created before such a change have
volumes_info entries whose 'disk_id' no longer matches any disk 'id'
in a freshly-fetched export_info, which would lead the destination
provider to consider the existing volumes orphaned (deleting them)
and to create brand new ones.

To allow safely updating such replicas, each export_info disk may
declare the identifiers it was previously reported under via its
'legacy_ids' field. Any volumes_info entry whose 'disk_id' does not
match a current disk 'id' but does match a legacy one is updated
in-place to the current 'id'.
"""
disks = export_info.get('devices', {}).get('disks', [])
current_ids = {str(d['id']) for d in disks if d.get('id')}
legacy_ids_map = {}
for disk in disks:
disk_id = disk.get('id')
if not disk_id:
continue
for legacy_id in disk.get('legacy_ids') or []:
legacy_ids_map[str(legacy_id)] = str(disk_id)

existing_volume_disk_ids = {
str(v.get('disk_id')) for v in volumes_info}
for volume_info in volumes_info:
disk_id = str(volume_info.get('disk_id'))
if disk_id in current_ids:
continue
new_disk_id = legacy_ids_map.get(disk_id)
if not new_disk_id:
continue
if new_disk_id in existing_volume_disk_ids:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd edge-case, basically pretending like a volume can be attached twice to the same VM. Or there's a UUID conflict. It's fine for now.

# Migrating would duplicate an already-present entry; leave
# the legacy-keyed one for the destination provider to clean
# up as an orphaned volume instead.
LOG.warning(
"Not migrating volumes_info entry with legacy disk_id "
"'%s' to '%s': an entry with the new id already exists.",
disk_id, new_disk_id)
continue
LOG.info(
"Migrating volumes_info entry from legacy disk_id '%s' to "
"'%s'.", disk_id, new_disk_id)
volume_info['disk_id'] = new_disk_id
existing_volume_disk_ids.add(new_disk_id)

return volumes_info


def _preserve_old_export_info_nic_ips(old_export_info, new_export_info):
def _get_nic(nics_info, nic_id):
for nic_info in nics_info:
Expand Down Expand Up @@ -291,6 +345,12 @@ def _run(self, ctxt, instance, origin, destination, task_info,
connection_info = base.get_connection_info(ctxt, destination)

volumes_info = task_info.get("volumes_info", [])
# NOTE: migrate any volumes_info entries of pre-existing replicas
# which are still keyed on a legacy disk identifier (e.g. VMware
# device keys before the switch to virtual disk UUIDs) before the
# provider matches them against the current export_info disk ids:
_update_legacy_disk_ids_in_volumes_info(export_info, volumes_info)

volumes_info = provider.deploy_replica_disks(
ctxt, connection_info, target_environment, instance, export_info,
volumes_info)
Expand Down
Loading