Skip to content

Commit 6433f4e

Browse files
committed
Add clustered migration sync for shared disks (SYNCING barrier)
Introduce TASK_STATUS_SYNCING and TASK_TYPES_TO_SYNC (GET_INSTANCE_INFO, DEPLOY_TRANSFER_DISKS, REPLICATE_DISKS) so multi-instance transfers with base_transfer_action.clustered=True wait for all peer tasks of the same type before leaving SYNCING for COMPLETED and advancing dependents. - clustered is set as len(instances) > 1 on transfer create - On task_completed: enter SYNCING when the barrier applies, then when every peer is SYNCING, run sync hooks (GET_INSTANCE_INFO: promote shareable on export disks, DEPLOY_TRANSFER_DISKS: shared-disk volumes_info, REPLICATE_DISKS: sync change_id) - ReplicateDisksTask: skip provider replicate for replicate_disk_data=False - On task error: abort peers stuck in SYNCING for the same task type
1 parent dddd294 commit 6433f4e

20 files changed

Lines changed: 736 additions & 126 deletions

coriolis/api-refs/api_samples/transfer/openstack-transfer-create-resp.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
}
5050
},
5151
"executions": [],
52-
"scenario": "replica"
52+
"scenario": "replica",
53+
"clustered": false
5354
}
5455
}

coriolis/api-refs/api_samples/transfer/openstack-transfer-get-resp.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"origin_minion_pool_id": null,
4343
"destination_minion_pool_id": null,
4444
"instance_osmorphing_minion_pool_mappings": {},
45+
"clustered": false,
4546
"executions": [
4647
{
4748
"created_at": "2019-07-11T10:06:47.000000",

coriolis/api-refs/api_samples/transfer/transfer-list-resp.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
"instances": {}
7575
},
7676
"id": "0460aa4d-6b16-4c98-bd56-27ee186e4a22",
77-
"scenario": "replica"
77+
"scenario": "replica",
78+
"clustered": false
7879
}
7980
]
8081
}

coriolis/api-refs/api_samples/transfer/transfer-update-resp.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@
133133
"ubuntu-xenial": "echo 'anything you need'"
134134
}
135135
},
136-
"scenario": "replica"
136+
"scenario": "replica",
137+
"clustered": false
137138
}
138139
}
139140
}

coriolis/api-refs/source/parameters.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ connection_info_schema:
130130
in: body
131131
type: object
132132
required: false
133+
clustered:
134+
description: |
135+
Present on transfer responses. ``true`` when more than one instance is
136+
listed (multi-instance scheduling: sync barriers and shared-disk
137+
coordination). Set by the server at creation from ``instances``; not
138+
accepted on create.
139+
in: body
140+
type: boolean
141+
required: false
133142
deployment_cancel:
134143
description: |
135144
Object containing information about the type of deployment cancellation.

coriolis/api-refs/source/transfer.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Response
5151
- instance_osmorphing_minion_pool_mappings : instance_osmorphing_minion_pool_mappings
5252
- user_scripts : user_scripts
5353
- scenario: scenario_type
54+
- clustered : clustered
5455

5556
**Example of Transfer List Response**
5657

@@ -111,6 +112,7 @@ Response
111112
- instance_osmorphing_minion_pool_mappings : instance_osmorphing_minion_pool_mappings
112113
- user_scripts : user_scripts
113114
- scenario: scenario_type
115+
- clustered : clustered
114116

115117
**Example of Transfer Show Response**
116118

@@ -183,6 +185,7 @@ Response
183185
- instance_osmorphing_minion_pool_mappings : instance_osmorphing_minion_pool_mappings
184186
- user_scripts : user_scripts
185187
- scenario: scenario_type
188+
- clustered : clustered
186189

187190
**Example of Transfer Create Response**
188191

coriolis/conductor/rpc/server.py

Lines changed: 522 additions & 114 deletions
Large diffs are not rendered by default.

coriolis/constants.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@
5050
TASK_STATUS_CANCELED_FROM_DEADLOCK = "STRANDED_AFTER_DEADLOCK"
5151
TASK_STATUS_ON_ERROR_ONLY = "EXECUTE_ON_ERROR_ONLY"
5252
TASK_STATUS_FAILED_TO_SCHEDULE = "FAILED_TO_SCHEDULE"
53+
TASK_STATUS_SYNCING = "SYNCING"
5354

5455
ACTIVE_TASK_STATUSES = [
5556
TASK_STATUS_PENDING,
5657
TASK_STATUS_STARTING,
5758
TASK_STATUS_RUNNING,
59+
TASK_STATUS_SYNCING,
5860
TASK_STATUS_CANCELLING,
5961
TASK_STATUS_CANCELLING_AFTER_COMPLETION
6062
]
@@ -161,6 +163,11 @@
161163
TASK_TYPE_POWER_ON_DESTINATION_MINION = "POWER_ON_DESTINATION_MINION"
162164
TASK_TYPE_POWER_OFF_DESTINATION_MINION = "POWER_OFF_DESTINATION_MINION"
163165

166+
TASK_TYPES_TO_SYNC = [
167+
TASK_TYPE_GET_INSTANCE_INFO,
168+
TASK_TYPE_DEPLOY_TRANSFER_DISKS,
169+
TASK_TYPE_SHUTDOWN_INSTANCE,
170+
]
164171

165172
MINION_POOL_OPERATIONS_TASKS = [
166173
TASK_TYPE_VALIDATE_SOURCE_MINION_POOL_OPTIONS,
@@ -219,6 +226,7 @@
219226
DISK_FORMAT_QCOW2 = 'qcow2'
220227
DISK_FORMAT_VHD = 'vhd'
221228
DISK_FORMAT_VHDX = 'vhdx'
229+
VOLUME_INFO_REPLICATE_DISK_DATA = "replicate_disk_data"
222230

223231
DISK_ALLOCATION_TYPE_STATIC = "static"
224232
DISK_ALLOCATION_TYPE_DYNAMIC = "dynamic"

coriolis/db/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ def update_transfer_action_info_for_instance(
727727
""" Updates the info for the given action with the provided dict.
728728
Returns the updated value.
729729
Sub-fields of the dict already in the info will get overwritten entirely!
730+
After merging, volumes_info is updated so it stays aligned with export_info
730731
"""
731732
action = get_action(context, action_id, include_task_info=True)
732733
if not new_instance_info:
@@ -759,6 +760,7 @@ def update_transfer_action_info_for_instance(
759760

760761
instance_info_old_copy = instance_info_old.copy()
761762
instance_info_old_copy.update(new_instance_info)
763+
utils.sync_instance_volumes_with_export(instance_info_old_copy)
762764
action_info[instance] = instance_info_old_copy
763765
action.info = action_info
764766

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2026 Cloudbase Solutions Srl
2+
# All Rights Reserved.
3+
4+
import sqlalchemy
5+
6+
7+
def upgrade(migrate_engine):
8+
meta = sqlalchemy.MetaData()
9+
meta.bind = migrate_engine
10+
11+
base_transfer = sqlalchemy.Table(
12+
'base_transfer_action', meta, autoload=True)
13+
if 'clustered' in base_transfer.c:
14+
return
15+
# server_default so existing rows get a value when the column is added
16+
# (MySQL stores booleans as TINYINT).
17+
clustered = sqlalchemy.Column(
18+
'clustered', sqlalchemy.Boolean, nullable=False,
19+
server_default=sqlalchemy.text('0'))
20+
base_transfer.create_column(clustered)

0 commit comments

Comments
 (0)