Skip to content

Commit 98d37f4

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 1a9f913 commit 98d37f4

19 files changed

Lines changed: 734 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
@@ -123,6 +123,15 @@ connection_info_schema:
123123
in: body
124124
type: object
125125
required: false
126+
clustered:
127+
description: |
128+
Present on transfer responses. ``true`` when more than one instance is
129+
listed (multi-instance scheduling: sync barriers and shared-disk
130+
coordination). Set by the server at creation from ``instances``; not
131+
accepted on create.
132+
in: body
133+
type: boolean
134+
required: false
126135
deployment_cancel:
127136
description: |
128137
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,
@@ -218,6 +225,7 @@
218225
DISK_FORMAT_QCOW2 = 'qcow2'
219226
DISK_FORMAT_VHD = 'vhd'
220227
DISK_FORMAT_VHDX = 'vhdx'
228+
VOLUME_INFO_REPLICATE_DISK_DATA = "replicate_disk_data"
221229

222230
DISK_ALLOCATION_TYPE_STATIC = "static"
223231
DISK_ALLOCATION_TYPE_DYNAMIC = "dynamic"

coriolis/db/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ def update_transfer_action_info_for_instance(
662662
""" Updates the info for the given action with the provided dict.
663663
Returns the updated value.
664664
Sub-fields of the dict already in the info will get overwritten entirely!
665+
After merging, volumes_info is updated so it stays aligned with export_info
665666
"""
666667
action = get_action(context, action_id, include_task_info=True)
667668
if not new_instance_info:
@@ -694,6 +695,7 @@ def update_transfer_action_info_for_instance(
694695

695696
instance_info_old_copy = instance_info_old.copy()
696697
instance_info_old_copy.update(new_instance_info)
698+
utils.sync_instance_volumes_with_export(instance_info_old_copy)
697699
action_info[instance] = instance_info_old_copy
698700
action.info = action_info
699701

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)