Skip to content

Commit f1476b1

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 2b4c293 commit f1476b1

19 files changed

Lines changed: 655 additions & 125 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: 482 additions & 113 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
@@ -55,11 +55,13 @@
5555
TASK_STATUS_CANCELED_FROM_DEADLOCK = "STRANDED_AFTER_DEADLOCK"
5656
TASK_STATUS_ON_ERROR_ONLY = "EXECUTE_ON_ERROR_ONLY"
5757
TASK_STATUS_FAILED_TO_SCHEDULE = "FAILED_TO_SCHEDULE"
58+
TASK_STATUS_SYNCING = "SYNCING"
5859

5960
ACTIVE_TASK_STATUSES = [
6061
TASK_STATUS_PENDING,
6162
TASK_STATUS_STARTING,
6263
TASK_STATUS_RUNNING,
64+
TASK_STATUS_SYNCING,
6365
TASK_STATUS_CANCELLING,
6466
TASK_STATUS_CANCELLING_AFTER_COMPLETION
6567
]
@@ -182,6 +184,11 @@
182184
TASK_TYPE_POWER_ON_DESTINATION_MINION = "POWER_ON_DESTINATION_MINION"
183185
TASK_TYPE_POWER_OFF_DESTINATION_MINION = "POWER_OFF_DESTINATION_MINION"
184186

187+
TASK_TYPES_TO_SYNC = [
188+
TASK_TYPE_GET_INSTANCE_INFO,
189+
TASK_TYPE_DEPLOY_TRANSFER_DISKS,
190+
TASK_TYPE_SHUTDOWN_INSTANCE,
191+
]
185192

186193
MINION_POOL_OPERATIONS_TASKS = [
187194
TASK_TYPE_VALIDATE_SOURCE_MINION_POOL_OPTIONS,
@@ -240,6 +247,7 @@
240247
DISK_FORMAT_QCOW2 = 'qcow2'
241248
DISK_FORMAT_VHD = 'vhd'
242249
DISK_FORMAT_VHDX = 'vhdx'
250+
VOLUME_INFO_REPLICATE_DISK_DATA = "replicate_disk_data"
243251

244252
DISK_ALLOCATION_TYPE_STATIC = "static"
245253
DISK_ALLOCATION_TYPE_DYNAMIC = "dynamic"

coriolis/db/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,7 @@ def update_transfer_action_info_for_instance(
762762
""" Updates the info for the given action with the provided dict.
763763
Returns the updated value.
764764
Sub-fields of the dict already in the info will get overwritten entirely!
765+
After merging, volumes_info is updated so it stays aligned with export_info
765766
"""
766767
action = get_action(context, action_id, include_task_info=True)
767768
if not new_instance_info:
@@ -794,6 +795,7 @@ def update_transfer_action_info_for_instance(
794795

795796
instance_info_old_copy = instance_info_old.copy()
796797
instance_info_old_copy.update(new_instance_info)
798+
utils.sync_instance_volumes_with_export(instance_info_old_copy)
797799
action_info[instance] = instance_info_old_copy
798800
action.info = action_info
799801

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)