Add xenserver.create.full.clone global setting

Adds a StoragePool-scoped boolean ConfigKey mirroring vmware.create.full.clone
so XenServer-backed VMs can be deployed as full VDI copies (VDI.copy) instead
of always using linked clones (VDI.clone). Default false preserves today's
behavior.

The per-pool flag flows into the existing PrimaryDataStoreTO.fullCloneFlag
through a new dispatch method addFullCloneAndDiskprovisiongStrictnessFlagOnDest
that switches on hypervisor type.
This commit is contained in:
Erki Märks 2026-05-07 11:33:15 +03:00
parent a4a52c9665
commit 06c6651d36
5 changed files with 69 additions and 8 deletions

View File

@ -195,6 +195,14 @@ public interface StorageManager extends StorageService {
true,
ConfigKey.Scope.StoragePool,
null);
ConfigKey<Boolean> XenserverCreateCloneFull = new ConfigKey<>(Boolean.class,
"xenserver.create.full.clone",
"Storage",
"false",
"If set to true, creates VMs as full clones on XenServer hypervisor (uses VDI.copy instead of VDI.clone, removing the linked-clone parent relationship).",
true,
ConfigKey.Scope.StoragePool,
null);
ConfigKey<Boolean> VmwareAllowParallelExecution = new ConfigKey<>(Boolean.class,
"vmware.allow.parallel.command.execution",
"Advanced",

View File

@ -187,7 +187,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
srcForCopy = cacheData = cacheMgr.createCacheObject(srcData, destScope);
}
CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()), primaryStorageDownloadWait,
CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnDest(destData.getTO()), primaryStorageDownloadWait,
VirtualMachineManager.ExecuteInSequence.value());
EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcForCopy, destData);
if (ep == null) {
@ -253,6 +253,43 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
return dataTO;
}
/**
* Adds {@code 'xenserver.create.full.clone'} value for a given primary storage, whose HV is XenServer, on datastore's {@code fullCloneFlag} field
* @param dataTO Dest data store TO
* @return dataTO including fullCloneFlag, if provided
*/
protected DataTO addFullCloneAndDiskprovisiongStrictnessFlagOnXenServerDest(DataTO dataTO) {
if (dataTO != null && dataTO.getHypervisorType().equals(Hypervisor.HypervisorType.XenServer)){
DataStoreTO dataStoreTO = dataTO.getDataStore();
if (dataStoreTO != null && dataStoreTO instanceof PrimaryDataStoreTO){
PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) dataStoreTO;
primaryDataStoreTO.setFullCloneFlag(StorageManager.XenserverCreateCloneFull.valueIn(primaryDataStoreTO.getId()));
}
}
return dataTO;
}
/**
* Dispatches to the per-hypervisor {@code addFullCloneAndDiskprovisiongStrictnessFlagOn*Dest} helper
* based on {@code dataTO.getHypervisorType()}. Returns {@code dataTO} unchanged for hypervisors
* that do not have a full-clone toggle.
* @param dataTO Dest data store TO
* @return dataTO including fullCloneFlag, if provided
*/
protected DataTO addFullCloneAndDiskprovisiongStrictnessFlagOnDest(DataTO dataTO) {
if (dataTO == null) {
return dataTO;
}
switch (dataTO.getHypervisorType()) {
case VMware:
return addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(dataTO);
case XenServer:
return addFullCloneAndDiskprovisiongStrictnessFlagOnXenServerDest(dataTO);
default:
return dataTO;
}
}
protected Answer copyObject(DataObject srcData, DataObject destData) {
return copyObject(srcData, destData, null);
}
@ -309,7 +346,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
ep = selector.select(srcData, volObj);
}
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(volObj.getTO()), _createVolumeFromSnapshotWait, VirtualMachineManager.ExecuteInSequence.value());
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnDest(volObj.getTO()), _createVolumeFromSnapshotWait, VirtualMachineManager.ExecuteInSequence.value());
Answer answer = null;
if (ep == null) {
@ -332,7 +369,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
}
protected Answer cloneVolume(DataObject template, DataObject volume) {
CopyCommand cmd = new CopyCommand(template.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(volume.getTO()), 0, VirtualMachineManager.ExecuteInSequence.value());
CopyCommand cmd = new CopyCommand(template.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnDest(volume.getTO()), 0, VirtualMachineManager.ExecuteInSequence.value());
try {
EndPoint ep = selector.select(volume, anyVolumeRequiresEncryption(volume));
Answer answer = null;
@ -416,7 +453,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
objOnImageStore.processEvent(Event.CopyingRequested);
CopyCommand cmd = new CopyCommand(objOnImageStore.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()), _copyvolumewait, VirtualMachineManager.ExecuteInSequence.value());
CopyCommand cmd = new CopyCommand(objOnImageStore.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnDest(destData.getTO()), _copyvolumewait, VirtualMachineManager.ExecuteInSequence.value());
EndPoint ep = selector.select(objOnImageStore, destData, encryptionRequired);
if (ep == null) {
String errMsg = String.format(NO_REMOTE_ENDPOINT_WITH_ENCRYPTION, encryptionRequired);
@ -660,7 +697,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
ep = selector.select(srcData, destData);
}
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()), _createprivatetemplatefromsnapshotwait, VirtualMachineManager.ExecuteInSequence.value());
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnDest(destData.getTO()), _createprivatetemplatefromsnapshotwait, VirtualMachineManager.ExecuteInSequence.value());
Answer answer = null;
if (ep == null) {
logger.error(NO_REMOTE_ENDPOINT_SSVM);
@ -698,7 +735,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
Scope selectedScope = pickCacheScopeForCopy(srcData, destData);
cacheData = cacheMgr.getCacheObject(srcData, selectedScope);
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value());
CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnDest(destData.getTO()), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value());
cmd.setCacheTO(cacheData.getTO());
cmd.setOptions(options);
EndPoint ep = selector.select(srcData, destData, encryptionRequired);
@ -709,7 +746,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
answer = ep.sendMessage(cmd);
}
} else {
addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO());
addFullCloneAndDiskprovisiongStrictnessFlagOnDest(destData.getTO());
CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value());
cmd.setOptions(options);
EndPoint ep = selector.select(srcData, destData, StorageAction.BACKUPSNAPSHOT, encryptionRequired);

View File

@ -101,6 +101,14 @@ public class AncientDataMotionStrategyTest {
verify(dataStoreTO).setFullCloneFlag(FULL_CLONE_FLAG);
}
@Test
public void testAddFullCloneFlagOnXenServerDest() throws IllegalAccessException, NoSuchFieldException {
overrideDefaultConfigValue(StorageManager.XenserverCreateCloneFull, String.valueOf(FULL_CLONE_FLAG));
when(dataTO.getHypervisorType()).thenReturn(HypervisorType.XenServer);
strategy.addFullCloneAndDiskprovisiongStrictnessFlagOnXenServerDest(dataTO);
verify(dataStoreTO).setFullCloneFlag(FULL_CLONE_FLAG);
}
@Test
public void testAddFullCloneFlagOnNotVmwareDest(){
verify(dataStoreTO, never()).setFullCloneFlag(any(Boolean.class));

View File

@ -859,12 +859,19 @@ public class XenServerStorageProcessor implements StorageProcessor {
final DataTO srcData = cmd.getSrcTO();
final DataTO destData = cmd.getDestTO();
final VolumeObjectTO volume = (VolumeObjectTO) destData;
final DataStoreTO destStore = volume.getDataStore();
final boolean fullClone = destStore instanceof PrimaryDataStoreTO
&& Boolean.TRUE.equals(((PrimaryDataStoreTO) destStore).isFullCloneFlag());
VDI vdi = null;
try {
VDI tmpltvdi = null;
tmpltvdi = getVDIbyUuid(conn, srcData.getPath());
vdi = tmpltvdi.createClone(conn, new HashMap<String, String>());
if (fullClone) {
vdi = tmpltvdi.copy(conn, tmpltvdi.getSR(conn));
} else {
vdi = tmpltvdi.createClone(conn, new HashMap<String, String>());
}
Long virtualSize = vdi.getVirtualSize(conn);
if (volume.getSize() > virtualSize) {
logger.debug("Overriding provided Template's size with new size " + toHumanReadableSize(volume.getSize()) + " for volume: " + volume.getName());

View File

@ -4616,6 +4616,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
SecStorageVMAutoScaleDown,
MountDisabledStoragePool,
VmwareCreateCloneFull,
XenserverCreateCloneFull,
VmwareAllowParallelExecution,
DataStoreDownloadFollowRedirects,
AllowVolumeReSizeBeyondAllocation,