diff --git a/coriolis/providers/base.py b/coriolis/providers/base.py index 8370bf3d..d12a3cfd 100644 --- a/coriolis/providers/base.py +++ b/coriolis/providers/base.py @@ -22,6 +22,23 @@ def platform(self) -> str: """Platform type.""" raise NotImplementedError("Missing provider platform attribute.") + @classmethod + def supports_shared_disks(cls) -> bool: + """Whether this provider supports shared (multi-writer) disks. + + Destination import providers that can attach the same volume to + multiple instances (e.g. Libvirt ````) should override + this to return ``True``. Defaults to ``False``. + + In order to coordinate shared disk operations, the conductor will mark + one of the instances as owner of the shared disk. + + As such, operations such as disk creation, deletion, resize or + data migration should be handled by tasks that are targeting the + owner instance. + """ + return False + class BaseEndpointProvider(BaseProvider): diff --git a/coriolis/tasks/replica_tasks.py b/coriolis/tasks/replica_tasks.py index 2e886047..a9178c78 100644 --- a/coriolis/tasks/replica_tasks.py +++ b/coriolis/tasks/replica_tasks.py @@ -1072,6 +1072,16 @@ def _run(self, ctxt, instance, origin, destination, task_info, "Replica Import validation for destination platform " "'%s'" % destination_type) + disks = export_info.get("devices", {}).get("disks") or [] + if any(d.get("owner") and d.get("owner") != instance + for d in disks if d): + if not destination_provider.supports_shared_disks(): + raise exception.NotSupportedOperation( + operation=( + "Destination platform '%s' does not support " + "synchronized transfer of instances that have " + "shared disks" % destination_type)) + target_environment = task_info["target_environment"] self._validate_provider_replica_import_input( destination_provider, ctxt, destination_connection_info,