Skip to content

Commit c8651f8

Browse files
author
Mike Tutkowski
committed
Support online migration of a virtual disk on XenServer from non-managed storage to managed storage
1 parent 4434901 commit c8651f8

13 files changed

Lines changed: 1047 additions & 150 deletions

File tree

core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,18 @@ public DataTO getDestData() {
9797
return destData;
9898
}
9999

100+
public void setSrcDetails(Map<String, String> details) {
101+
srcDetails = details;
102+
}
103+
100104
public Map<String, String> getSrcDetails() {
101105
return srcDetails;
102106
}
103107

108+
public void setDestDetails(Map<String, String> details) {
109+
destDetails = details;
110+
}
111+
104112
public Map<String, String> getDestDetails() {
105113
return destDetails;
106114
}

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
import com.cloud.storage.StoragePool;
2626

2727
public interface PrimaryDataStoreDriver extends DataStoreDriver {
28+
enum QualityOfServiceState { MIGRATION, NO_MIGRATION }
29+
2830
String BASIC_CREATE = "basicCreate";
2931
String BASIC_DELETE = "basicDelete";
3032
String BASIC_DELETE_FAILURE = "basicDeleteFailure";
33+
String BASIC_DELETE_BY_FOLDER = "basicDeleteByFolder";
3134
String BASIC_GRANT_ACCESS = "basicGrantAccess";
3235
String BASIC_REVOKE_ACCESS = "basicRevokeAccess";
3336
String BASIC_IQN = "basicIqn";
@@ -67,4 +70,6 @@ public interface PrimaryDataStoreDriver extends DataStoreDriver {
6770
void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback);
6871

6972
void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback);
73+
74+
void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState);
7075
}

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java

Lines changed: 402 additions & 101 deletions
Large diffs are not rendered by default.

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java

Lines changed: 120 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,67 +1517,114 @@ public Answer createTemplateFromSnapshot(final CopyCommand cmd) {
15171517
}
15181518
}
15191519

1520+
private boolean isManaged(Map<String, String> options) {
1521+
if (options == null) {
1522+
return false;
1523+
}
1524+
1525+
String iqn = options.get(DiskTO.IQN);
1526+
1527+
if (iqn == null || iqn.trim().length() == 0) {
1528+
return false;
1529+
}
1530+
1531+
String storageHost = options.get(DiskTO.STORAGE_HOST);
1532+
1533+
if (storageHost == null || storageHost.trim().length() == 0) {
1534+
return false;
1535+
}
1536+
1537+
return true;
1538+
}
1539+
1540+
boolean isCreateManagedVolumeFromManagedSnapshot(Map<String, String> volumeOptions, Map<String, String> snapshotOptions) {
1541+
return isManaged(volumeOptions) && isManaged(snapshotOptions);
1542+
}
1543+
1544+
boolean isCreateNonManagedVolumeFromManagedSnapshot(Map<String, String> volumeOptions, Map<String, String> snapshotOptions) {
1545+
return !isManaged(volumeOptions) && isManaged(snapshotOptions);
1546+
}
1547+
15201548
@Override
15211549
public Answer createVolumeFromSnapshot(final CopyCommand cmd) {
1522-
final Connection conn = hypervisorResource.getConnection();
1523-
final DataTO srcData = cmd.getSrcTO();
1524-
final SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData;
1525-
final DataTO destData = cmd.getDestTO();
1526-
final DataStoreTO imageStore = srcData.getDataStore();
1550+
Connection conn = hypervisorResource.getConnection();
1551+
1552+
DataTO srcData = cmd.getSrcTO();
1553+
SnapshotObjectTO snapshot = (SnapshotObjectTO)srcData;
1554+
DataStoreTO imageStore = srcData.getDataStore();
1555+
DataTO destData = cmd.getDestTO();
15271556

1528-
if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
1529-
return createVolumeFromSnapshot2(cmd);
1557+
if (isCreateManagedVolumeFromManagedSnapshot(cmd.getOptions2(), cmd.getOptions())) {
1558+
return createManagedVolumeFromManagedSnapshot(cmd);
1559+
}
1560+
1561+
if (isCreateNonManagedVolumeFromManagedSnapshot(cmd.getOptions2(), cmd.getOptions())) {
1562+
return createNonManagedVolumeFromManagedSnapshot(cmd);
15301563
}
15311564

15321565
if (!(imageStore instanceof NfsTO)) {
15331566
return new CopyCmdAnswer("unsupported protocol");
15341567
}
15351568

1536-
final NfsTO nfsImageStore = (NfsTO) imageStore;
1537-
final String primaryStorageNameLabel = destData.getDataStore().getUuid();
1538-
final String secondaryStorageUrl = nfsImageStore.getUrl();
1539-
final int wait = cmd.getWait();
1569+
NfsTO nfsImageStore = (NfsTO)imageStore;
1570+
String primaryStorageNameLabel = destData.getDataStore().getUuid();
1571+
String secondaryStorageUrl = nfsImageStore.getUrl();
1572+
1573+
int wait = cmd.getWait();
15401574
boolean result = false;
1575+
15411576
// Generic error message.
1542-
String details = null;
1543-
String volumeUUID = null;
1577+
String details;
1578+
String volumeUUID;
15441579

15451580
if (secondaryStorageUrl == null) {
1546-
details += " because the URL passed: " + secondaryStorageUrl + " is invalid.";
1581+
details = "The URL passed in 'null'.";
1582+
15471583
return new CopyCmdAnswer(details);
15481584
}
1585+
15491586
try {
1550-
final SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel);
1587+
SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel);
1588+
15511589
if (primaryStorageSR == null) {
1552-
throw new InternalErrorException("Could not create volume from snapshot because the primary Storage SR could not be created from the name label: " +
1553-
primaryStorageNameLabel);
1590+
throw new InternalErrorException("Could not create volume from snapshot because the primary storage SR could not be " +
1591+
"created from the name label: " + primaryStorageNameLabel);
15541592
}
1593+
15551594
// Get the absolute path of the snapshot on the secondary storage.
15561595
String snapshotInstallPath = snapshot.getPath();
1557-
final int index = snapshotInstallPath.lastIndexOf(nfsImageStore.getPathSeparator());
1558-
final String snapshotName = snapshotInstallPath.substring(index + 1);
1596+
int index = snapshotInstallPath.lastIndexOf(nfsImageStore.getPathSeparator());
1597+
String snapshotName = snapshotInstallPath.substring(index + 1);
15591598

15601599
if (!snapshotName.startsWith("VHD-") && !snapshotName.endsWith(".vhd")) {
15611600
snapshotInstallPath = snapshotInstallPath + ".vhd";
15621601
}
1563-
final URI snapshotURI = new URI(secondaryStorageUrl + nfsImageStore.getPathSeparator() + snapshotInstallPath);
1564-
final String snapshotPath = snapshotURI.getHost() + ":" + snapshotURI.getPath();
1565-
final String srUuid = primaryStorageSR.getUuid(conn);
1602+
1603+
URI snapshotURI = new URI(secondaryStorageUrl + nfsImageStore.getPathSeparator() + snapshotInstallPath);
1604+
String snapshotPath = snapshotURI.getHost() + ":" + snapshotURI.getPath();
1605+
String srUuid = primaryStorageSR.getUuid(conn);
1606+
15661607
volumeUUID = copy_vhd_from_secondarystorage(conn, snapshotPath, srUuid, wait);
15671608
result = true;
1568-
final VDI volume = VDI.getByUuid(conn, volumeUUID);
1569-
final VDI.Record vdir = volume.getRecord(conn);
1570-
final VolumeObjectTO newVol = new VolumeObjectTO();
1609+
1610+
VDI volume = VDI.getByUuid(conn, volumeUUID);
1611+
VDI.Record vdir = volume.getRecord(conn);
1612+
VolumeObjectTO newVol = new VolumeObjectTO();
1613+
15711614
newVol.setPath(volumeUUID);
15721615
newVol.setSize(vdir.virtualSize);
1616+
15731617
return new CopyCmdAnswer(newVol);
15741618
} catch (final XenAPIException e) {
1575-
details += " due to " + e.toString();
1619+
details = "Exception due to " + e.toString();
1620+
15761621
s_logger.warn(details, e);
15771622
} catch (final Exception e) {
1578-
details += " due to " + e.getMessage();
1623+
details = "Exception due to " + e.getMessage();
1624+
15791625
s_logger.warn(details, e);
15801626
}
1627+
15811628
if (!result) {
15821629
// Is this logged at a higher level?
15831630
s_logger.error(details);
@@ -1587,7 +1634,7 @@ public Answer createVolumeFromSnapshot(final CopyCommand cmd) {
15871634
return new CopyCmdAnswer(details);
15881635
}
15891636

1590-
protected Answer createVolumeFromSnapshot2(final CopyCommand cmd) {
1637+
Answer createManagedVolumeFromManagedSnapshot(final CopyCommand cmd) {
15911638
try {
15921639
final Connection conn = hypervisorResource.getConnection();
15931640

@@ -1632,6 +1679,51 @@ protected Answer createVolumeFromSnapshot2(final CopyCommand cmd) {
16321679
}
16331680
}
16341681

1682+
Answer createNonManagedVolumeFromManagedSnapshot(final CopyCommand cmd) {
1683+
Connection conn = hypervisorResource.getConnection();
1684+
SR srcSr = null;
1685+
1686+
try {
1687+
Map<String, String> srcOptions = cmd.getOptions();
1688+
1689+
String src_iScsiName = srcOptions.get(DiskTO.IQN);
1690+
String srcStorageHost = srcOptions.get(DiskTO.STORAGE_HOST);
1691+
String srcChapInitiatorUsername = srcOptions.get(DiskTO.CHAP_INITIATOR_USERNAME);
1692+
String srcChapInitiatorSecret = srcOptions.get(DiskTO.CHAP_INITIATOR_SECRET);
1693+
1694+
srcSr = hypervisorResource.getIscsiSR(conn, src_iScsiName, srcStorageHost, src_iScsiName,
1695+
srcChapInitiatorUsername, srcChapInitiatorSecret, false);
1696+
1697+
// there should only be one VDI in this SR
1698+
VDI srcVdi = srcSr.getVDIs(conn).iterator().next();
1699+
1700+
DataTO destData = cmd.getDestTO();
1701+
String primaryStorageNameLabel = destData.getDataStore().getUuid();
1702+
1703+
SR destSr = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel);
1704+
1705+
VDI vdiCopy = srcVdi.copy(conn, destSr);
1706+
1707+
VolumeObjectTO newVol = new VolumeObjectTO();
1708+
1709+
newVol.setSize(vdiCopy.getVirtualSize(conn));
1710+
newVol.setPath(vdiCopy.getUuid(conn));
1711+
newVol.setFormat(ImageFormat.VHD);
1712+
1713+
return new CopyCmdAnswer(newVol);
1714+
}
1715+
catch (Exception ex) {
1716+
s_logger.warn("Failed to copy snapshot to volume: " + ex.toString(), ex);
1717+
1718+
return new CopyCmdAnswer(ex.getMessage());
1719+
}
1720+
finally {
1721+
if (srcSr != null) {
1722+
hypervisorResource.removeSR(conn, srcSr);
1723+
}
1724+
}
1725+
}
1726+
16351727
@Override
16361728
public Answer deleteSnapshot(final DeleteCommand cmd) {
16371729
final SnapshotObjectTO snapshot = (SnapshotObjectTO) cmd.getData();

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,12 @@ public Answer createVolumeFromSnapshot(final CopyCommand cmd) {
787787
final VolumeObjectTO volume = (VolumeObjectTO)destData;
788788
final DataStoreTO imageStore = srcData.getDataStore();
789789

790-
if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
791-
return createVolumeFromSnapshot2(cmd);
790+
if (isCreateManagedVolumeFromManagedSnapshot(cmd.getOptions2(), cmd.getOptions())) {
791+
return createManagedVolumeFromManagedSnapshot(cmd);
792+
}
793+
794+
if (isCreateNonManagedVolumeFromManagedSnapshot(cmd.getOptions2(), cmd.getOptions())) {
795+
return createNonManagedVolumeFromManagedSnapshot(cmd);
792796
}
793797

794798
if (!(imageStore instanceof NfsTO)) {

plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateVolumeCommandWrapper.java

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.cloud.agent.api.Answer;
2828
import com.cloud.agent.api.storage.MigrateVolumeAnswer;
2929
import com.cloud.agent.api.storage.MigrateVolumeCommand;
30+
import com.cloud.agent.api.to.DiskTO;
3031
import com.cloud.agent.api.to.StorageFilerTO;
3132
import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
3233
import com.cloud.resource.CommandWrapper;
@@ -39,35 +40,59 @@
3940

4041
@ResourceWrapper(handles = MigrateVolumeCommand.class)
4142
public final class XenServer610MigrateVolumeCommandWrapper extends CommandWrapper<MigrateVolumeCommand, Answer, XenServer610Resource> {
42-
43-
private static final Logger s_logger = Logger.getLogger(XenServer610MigrateVolumeCommandWrapper.class);
43+
private static final Logger LOGGER = Logger.getLogger(XenServer610MigrateVolumeCommandWrapper.class);
4444

4545
@Override
4646
public Answer execute(final MigrateVolumeCommand command, final XenServer610Resource xenServer610Resource) {
47-
final Connection connection = xenServer610Resource.getConnection();
48-
final String volumeUUID = command.getVolumePath();
49-
final StorageFilerTO poolTO = command.getPool();
47+
Connection connection = xenServer610Resource.getConnection();
48+
String srcVolumeUuid = command.getVolumePath();
49+
SR destPool = null;
50+
Map<String, String> destDetails = command.getDestDetails();
5051

5152
try {
52-
final String uuid = poolTO.getUuid();
53-
final SR destinationPool = xenServer610Resource.getStorageRepository(connection, uuid);
54-
final VDI srcVolume = xenServer610Resource.getVDIbyUuid(connection, volumeUUID);
55-
final Map<String, String> other = new HashMap<String, String>();
53+
VDI srcVolume = xenServer610Resource.getVDIbyUuid(connection, srcVolumeUuid);
54+
55+
if (destDetails != null && Boolean.parseBoolean(destDetails.get(DiskTO.MANAGED))) {
56+
String iScsiName = destDetails.get(DiskTO.IQN);
57+
String storageHost = destDetails.get(DiskTO.STORAGE_HOST);
58+
String chapInitiatorUsername = destDetails.get(DiskTO.CHAP_INITIATOR_USERNAME);
59+
String chapInitiatorSecret = destDetails.get(DiskTO.CHAP_INITIATOR_SECRET);
60+
61+
destPool = xenServer610Resource.getIscsiSR(connection, iScsiName, storageHost, iScsiName,
62+
chapInitiatorUsername, chapInitiatorSecret, false);
63+
}
64+
else {
65+
StorageFilerTO destPoolTO = command.getPool();
66+
String destPoolUuid = destPoolTO.getUuid();
67+
68+
destPool = xenServer610Resource.getStorageRepository(connection, destPoolUuid);
69+
}
70+
71+
Map<String, String> other = new HashMap<>();
72+
5673
other.put("live", "true");
5774

58-
// Live migrate the vdi across pool.
59-
final Task task = srcVolume.poolMigrateAsync(connection, destinationPool, other);
60-
final long timeout = xenServer610Resource.getMigrateWait() * 1000L;
75+
// Live migrate the VDI.
76+
Task task = srcVolume.poolMigrateAsync(connection, destPool, other);
77+
78+
long timeout = xenServer610Resource.getMigrateWait() * 1000L;
79+
6180
xenServer610Resource.waitForTask(connection, task, 1000, timeout);
6281
xenServer610Resource.checkForSuccess(connection, task);
6382

64-
final VDI dvdi = Types.toVDI(task, connection);
83+
VDI destVdi = Types.toVDI(task, connection);
84+
85+
return new MigrateVolumeAnswer(command, true, null, destVdi.getUuid(connection));
86+
} catch (Exception ex) {
87+
if (destDetails != null && Boolean.parseBoolean(destDetails.get(DiskTO.MANAGED)) && destPool != null) {
88+
xenServer610Resource.removeSR(connection, destPool);
89+
}
90+
91+
String msg = "Caught exception " + ex.getClass().getName() + " due to the following: " + ex.toString();
92+
93+
LOGGER.error(msg, ex);
6594

66-
return new MigrateVolumeAnswer(command, true, null, dvdi.getUuid(connection));
67-
} catch (final Exception e) {
68-
final String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString();
69-
s_logger.error(msg, e);
7095
return new MigrateVolumeAnswer(command, false, msg, null);
7196
}
7297
}
73-
}
98+
}

plugins/storage/volume/cloudbyte/src/main/java/org/apache/cloudstack/storage/datastore/driver/ElastistorPrimaryDataStoreDriver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> cal
321321

322322
}
323323

324+
@Override
325+
public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) {}
326+
324327
//this method will utilize the volume details table to add third party volume properties
325328
public void updateVolumeDetails(VolumeVO volume, FileSystem esvolume) {
326329

plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,4 +385,7 @@ public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> cal
385385

386386
callback.complete(result);
387387
}
388+
389+
@Override
390+
public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) {}
388391
}

plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,7 @@ public boolean canCopy(DataObject srcData, DataObject destData) {
206206

207207
@Override
208208
public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {}
209+
210+
@Override
211+
public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) {}
209212
}

plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
3333
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
3434
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
35+
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
3536
import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
3637
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
3738
import org.apache.cloudstack.framework.async.AsyncRpcContext;
@@ -227,6 +228,10 @@ public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCa
227228
public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
228229
}
229230

231+
@Override
232+
public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) {
233+
}
234+
230235
@Override
231236
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
232237
}

0 commit comments

Comments
 (0)