mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
make create template from volume/snapshot work
This commit is contained in:
parent
ac1b75dc9f
commit
5aeca646ae
@ -53,6 +53,7 @@ import org.apache.cloudstack.storage.command.CopyCommand;
|
|||||||
import org.apache.cloudstack.storage.command.DownloadCommand;
|
import org.apache.cloudstack.storage.command.DownloadCommand;
|
||||||
import org.apache.cloudstack.storage.command.DownloadProgressCommand;
|
import org.apache.cloudstack.storage.command.DownloadProgressCommand;
|
||||||
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
|
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
|
||||||
|
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
@ -103,6 +104,7 @@ import com.cloud.agent.api.to.SwiftTO;
|
|||||||
import com.cloud.exception.InternalErrorException;
|
import com.cloud.exception.InternalErrorException;
|
||||||
import com.cloud.host.Host;
|
import com.cloud.host.Host;
|
||||||
import com.cloud.host.Host.Type;
|
import com.cloud.host.Host.Type;
|
||||||
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.resource.ServerResourceBase;
|
import com.cloud.resource.ServerResourceBase;
|
||||||
import com.cloud.storage.DataStoreRole;
|
import com.cloud.storage.DataStoreRole;
|
||||||
import com.cloud.storage.StorageLayer;
|
import com.cloud.storage.StorageLayer;
|
||||||
@ -110,10 +112,14 @@ import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
|||||||
import com.cloud.storage.template.DownloadManager;
|
import com.cloud.storage.template.DownloadManager;
|
||||||
import com.cloud.storage.template.DownloadManagerImpl;
|
import com.cloud.storage.template.DownloadManagerImpl;
|
||||||
import com.cloud.storage.template.DownloadManagerImpl.ZfsPathParser;
|
import com.cloud.storage.template.DownloadManagerImpl.ZfsPathParser;
|
||||||
|
import com.cloud.storage.template.Processor.FormatInfo;
|
||||||
|
import com.cloud.storage.template.Processor;
|
||||||
|
import com.cloud.storage.template.QCOW2Processor;
|
||||||
import com.cloud.storage.template.TemplateLocation;
|
import com.cloud.storage.template.TemplateLocation;
|
||||||
import com.cloud.storage.template.TemplateProp;
|
import com.cloud.storage.template.TemplateProp;
|
||||||
import com.cloud.storage.template.UploadManager;
|
import com.cloud.storage.template.UploadManager;
|
||||||
import com.cloud.storage.template.UploadManagerImpl;
|
import com.cloud.storage.template.UploadManagerImpl;
|
||||||
|
import com.cloud.storage.template.VhdProcessor;
|
||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.S3Utils;
|
import com.cloud.utils.S3Utils;
|
||||||
import com.cloud.utils.S3Utils.FileNamingStrategy;
|
import com.cloud.utils.S3Utils.FileNamingStrategy;
|
||||||
@ -161,6 +167,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
|||||||
final private String _parent = "/mnt/SecStorage";
|
final private String _parent = "/mnt/SecStorage";
|
||||||
final private String _tmpltDir = "/var/cloudstack/template";
|
final private String _tmpltDir = "/var/cloudstack/template";
|
||||||
final private String _tmpltpp = "template.properties";
|
final private String _tmpltpp = "template.properties";
|
||||||
|
private String createTemplateFromSnapshotXenScript;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnected() {
|
public void disconnected() {
|
||||||
@ -281,10 +288,89 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Answer copyFromSwiftToNfs(CopyCommand cmd, DataTO srcData, SwiftTO srcImageStore,
|
|
||||||
|
|
||||||
DataTO destData, NfsTO destImageStore) {
|
protected Answer copySnapshotToTemplateFromNfsToNfsXenserver(CopyCommand cmd, SnapshotObjectTO srcData, NfsTO srcDataStore, TemplateObjectTO destData, NfsTO destDataStore) {
|
||||||
return Answer.createUnsupportedCommandAnswer(cmd);
|
String srcMountPoint = this.getRootDir(srcDataStore.getUrl());
|
||||||
|
String snapshotPath = srcData.getPath();
|
||||||
|
int index = snapshotPath.lastIndexOf("/");
|
||||||
|
String snapshotName = snapshotPath.substring(index + 1);
|
||||||
|
if (!snapshotName.startsWith("VHD-") && !snapshotName.endsWith(".vhd")) {
|
||||||
|
snapshotName = snapshotName + ".vhd";
|
||||||
|
}
|
||||||
|
snapshotPath = snapshotPath.substring(0, index);
|
||||||
|
snapshotPath = srcMountPoint + snapshotPath;
|
||||||
|
String destMountPoint = this.getRootDir(destDataStore.getUrl());
|
||||||
|
String destPath = destMountPoint + destData.getPath();
|
||||||
|
|
||||||
|
String errMsg = null;
|
||||||
|
try {
|
||||||
|
this._storage.mkdir(destPath);
|
||||||
|
|
||||||
|
String templateUuid = UUID.randomUUID().toString();
|
||||||
|
String templateName = templateUuid + ".vhd";
|
||||||
|
Script command = new Script(this.createTemplateFromSnapshotXenScript, cmd.getWait(), s_logger);
|
||||||
|
command.add("-p", snapshotPath);
|
||||||
|
command.add("-s", snapshotName);
|
||||||
|
command.add("-n", templateName);
|
||||||
|
command.add("-t", destPath);
|
||||||
|
command.execute();
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<String, Object>();
|
||||||
|
params.put(StorageLayer.InstanceConfigKey, _storage);
|
||||||
|
Processor processor = new VhdProcessor();
|
||||||
|
|
||||||
|
processor.configure("Vhd Processor", params);
|
||||||
|
FormatInfo info = processor.process(destPath, null,
|
||||||
|
templateUuid);
|
||||||
|
|
||||||
|
TemplateLocation loc = new TemplateLocation(_storage, destPath);
|
||||||
|
loc.create(1, true, templateName);
|
||||||
|
loc.addFormat(info);
|
||||||
|
loc.save();
|
||||||
|
|
||||||
|
TemplateObjectTO newTemplate = new TemplateObjectTO();
|
||||||
|
newTemplate.setPath(destData.getPath() + File.separator + templateUuid);
|
||||||
|
return new CopyCmdAnswer(newTemplate);
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
s_logger.debug("Failed to create template from snapshot: " + e.toString());
|
||||||
|
errMsg = e.toString();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
s_logger.debug("Failed to create template from snapshot: " + e.toString());
|
||||||
|
errMsg = e.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
s_logger.debug("Failed to create template from snapshot: " + e.toString());
|
||||||
|
errMsg = e.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CopyCmdAnswer(errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Answer copySnapshotToTemplateFromNfsToNfs(CopyCommand cmd, SnapshotObjectTO srcData, NfsTO srcDataStore, TemplateObjectTO destData, NfsTO destDataStore) {
|
||||||
|
|
||||||
|
if (srcData.getHypervisorType() == HypervisorType.XenServer) {
|
||||||
|
return copySnapshotToTemplateFromNfsToNfsXenserver(cmd, srcData, srcDataStore, destData, destDataStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CopyCmdAnswer("");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Answer createTemplateFromSnapshot(CopyCommand cmd) {
|
||||||
|
DataTO srcData = cmd.getSrcTO();
|
||||||
|
DataTO destData = cmd.getDestTO();
|
||||||
|
DataStoreTO srcDataStore = srcData.getDataStore();
|
||||||
|
DataStoreTO destDataStore = destData.getDataStore();
|
||||||
|
if (srcDataStore.getRole() == DataStoreRole.Image || srcDataStore.getRole() == DataStoreRole.ImageCache) {
|
||||||
|
if (!(srcDataStore instanceof NfsTO)) {
|
||||||
|
s_logger.debug("only support nfs storage as src, when create template from snapshot");
|
||||||
|
return Answer.createUnsupportedCommandAnswer(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destDataStore instanceof NfsTO){
|
||||||
|
return copySnapshotToTemplateFromNfsToNfs(cmd, (SnapshotObjectTO)srcData, (NfsTO)srcDataStore, (TemplateObjectTO)destData, (NfsTO)destDataStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return new CopyCmdAnswer("");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Answer execute(CopyCommand cmd) {
|
protected Answer execute(CopyCommand cmd) {
|
||||||
@ -293,22 +379,11 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
|||||||
DataStoreTO srcDataStore = srcData.getDataStore();
|
DataStoreTO srcDataStore = srcData.getDataStore();
|
||||||
DataStoreTO destDataStore = destData.getDataStore();
|
DataStoreTO destDataStore = destData.getDataStore();
|
||||||
|
|
||||||
if (srcDataStore.getRole() == DataStoreRole.Image && destDataStore.getRole() == DataStoreRole.ImageCache) {
|
if (srcData.getObjectType() == DataObjectType.SNAPSHOT && destData.getObjectType() == DataObjectType.TEMPLATE) {
|
||||||
|
return createTemplateFromSnapshot(cmd);
|
||||||
if (!(destDataStore instanceof NfsTO)) {
|
|
||||||
s_logger.debug("only support nfs as cache storage");
|
|
||||||
return Answer.createUnsupportedCommandAnswer(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcDataStore instanceof S3TO) {
|
|
||||||
return copyFromS3ToNfs(cmd, srcData, (S3TO) srcDataStore, destData, (NfsTO) destDataStore);
|
|
||||||
} else if (srcDataStore instanceof SwiftTO) {
|
|
||||||
return copyFromSwiftToNfs(cmd, srcData, (SwiftTO) srcDataStore, destData, (NfsTO) destDataStore);
|
|
||||||
} else {
|
|
||||||
return Answer.createUnsupportedCommandAnswer(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return Answer.createUnsupportedCommandAnswer(cmd);
|
return Answer.createUnsupportedCommandAnswer(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1653,6 +1728,11 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
|||||||
s_logger.info("_configIpFirewallScr found in " + _configIpFirewallScr);
|
s_logger.info("_configIpFirewallScr found in " + _configIpFirewallScr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createTemplateFromSnapshotXenScript = Script.findScript(getDefaultScriptsDir(), "create_privatetemplate_from_snapshot_xen.sh");
|
||||||
|
if (createTemplateFromSnapshotXenScript == null) {
|
||||||
|
throw new ConfigurationException("create_privatetemplate_from_snapshot_xen.sh not found in " + getDefaultScriptsDir());
|
||||||
|
}
|
||||||
|
|
||||||
_role = (String) params.get("role");
|
_role = (String) params.get("role");
|
||||||
if (_role == null)
|
if (_role == null)
|
||||||
_role = SecondaryStorageVm.Role.templateProcessor.toString();
|
_role = SecondaryStorageVm.Role.templateProcessor.toString();
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataTO;
|
|||||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||||
|
|
||||||
import com.cloud.agent.api.to.DataStoreTO;
|
import com.cloud.agent.api.to.DataStoreTO;
|
||||||
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
|
|
||||||
public class SnapshotObjectTO implements DataTO {
|
public class SnapshotObjectTO implements DataTO {
|
||||||
private String path;
|
private String path;
|
||||||
@ -13,6 +14,7 @@ public class SnapshotObjectTO implements DataTO {
|
|||||||
private DataStoreTO dataStore;
|
private DataStoreTO dataStore;
|
||||||
private String vmName;
|
private String vmName;
|
||||||
private String name;
|
private String name;
|
||||||
|
private HypervisorType hypervisorType;
|
||||||
private long id;
|
private long id;
|
||||||
|
|
||||||
public SnapshotObjectTO() {
|
public SnapshotObjectTO() {
|
||||||
@ -29,6 +31,7 @@ public class SnapshotObjectTO implements DataTO {
|
|||||||
}
|
}
|
||||||
this.dataStore = snapshot.getDataStore().getTO();
|
this.dataStore = snapshot.getDataStore().getTO();
|
||||||
this.setName(snapshot.getName());
|
this.setName(snapshot.getName());
|
||||||
|
this.hypervisorType = snapshot.getHypervisorType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -89,4 +92,12 @@ public class SnapshotObjectTO implements DataTO {
|
|||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HypervisorType getHypervisorType() {
|
||||||
|
return hypervisorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHypervisorType(HypervisorType hypervisorType) {
|
||||||
|
this.hypervisorType = hypervisorType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -327,7 +327,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
|
|||||||
.parseInt(Config.CreatePrivateTemplateFromSnapshotWait
|
.parseInt(Config.CreatePrivateTemplateFromSnapshotWait
|
||||||
.getDefaultValue()));
|
.getDefaultValue()));
|
||||||
|
|
||||||
if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) {
|
if (needCacheStorage(srcData, destData)) {
|
||||||
SnapshotInfo snapshot = (SnapshotInfo)srcData;
|
SnapshotInfo snapshot = (SnapshotInfo)srcData;
|
||||||
srcData = cacheSnapshotChain(snapshot);
|
srcData = cacheSnapshotChain(snapshot);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,6 +94,9 @@ public class CloudStackTestNGBase extends AbstractTestNGSpringContextTests {
|
|||||||
this.s3TemplateBucket = s3_template_bucket;
|
this.s3TemplateBucket = s3_template_bucket;
|
||||||
this.s3UseHttps = Boolean.parseBoolean(s3_usehttps);
|
this.s3UseHttps = Boolean.parseBoolean(s3_usehttps);
|
||||||
this.scriptPath = scriptPath;
|
this.scriptPath = scriptPath;
|
||||||
|
if (this.scriptPath != null) {
|
||||||
|
System.setProperty("paths.script", this.getScriptPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getHostGuid() {
|
protected String getHostGuid() {
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import javax.inject.Inject;
|
|||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||||
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||||
@ -45,6 +46,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreState
|
|||||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
|
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.type.RootDisk;
|
import org.apache.cloudstack.engine.subsystem.api.storage.type.RootDisk;
|
||||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||||
|
import org.apache.cloudstack.storage.LocalHostEndpoint;
|
||||||
|
import org.apache.cloudstack.storage.MockLocalNfsSecondaryStorageResource;
|
||||||
import org.apache.cloudstack.storage.RemoteHostEndPoint;
|
import org.apache.cloudstack.storage.RemoteHostEndPoint;
|
||||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||||
@ -144,6 +147,7 @@ public class SnapshotTest extends CloudStackTestNGBase {
|
|||||||
SnapshotDao snapshotDao;
|
SnapshotDao snapshotDao;
|
||||||
@Inject
|
@Inject
|
||||||
EndPointSelector epSelector;
|
EndPointSelector epSelector;
|
||||||
|
|
||||||
long primaryStoreId;
|
long primaryStoreId;
|
||||||
VMTemplateVO image;
|
VMTemplateVO image;
|
||||||
String imageStoreName = "testImageStore";
|
String imageStoreName = "testImageStore";
|
||||||
@ -196,7 +200,7 @@ public class SnapshotTest extends CloudStackTestNGBase {
|
|||||||
imageStore = new ImageStoreVO();
|
imageStore = new ImageStoreVO();
|
||||||
imageStore.setName(imageStoreName);
|
imageStore.setName(imageStoreName);
|
||||||
imageStore.setDataCenterId(dcId);
|
imageStore.setDataCenterId(dcId);
|
||||||
imageStore.setProviderName("CloudStack ImageStore Provider");
|
imageStore.setProviderName(DataStoreProvider.NFS_IMAGE);
|
||||||
imageStore.setRole(DataStoreRole.Image);
|
imageStore.setRole(DataStoreRole.Image);
|
||||||
imageStore.setUrl(this.getSecondaryStorage());
|
imageStore.setUrl(this.getSecondaryStorage());
|
||||||
imageStore.setUuid(UUID.randomUUID().toString());
|
imageStore.setUuid(UUID.randomUUID().toString());
|
||||||
@ -301,7 +305,7 @@ public class SnapshotTest extends CloudStackTestNGBase {
|
|||||||
pool.setPoolType(StoragePoolType.NetworkFilesystem);
|
pool.setPoolType(StoragePoolType.NetworkFilesystem);
|
||||||
pool.setPodId(podId);
|
pool.setPodId(podId);
|
||||||
pool.setScope(ScopeType.CLUSTER);
|
pool.setScope(ScopeType.CLUSTER);
|
||||||
pool.setStorageProviderName("cloudstack primary data store provider");
|
pool.setStorageProviderName(DataStoreProvider.DEFAULT_PRIMARY);
|
||||||
pool = this.primaryStoreDao.persist(pool);
|
pool = this.primaryStoreDao.persist(pool);
|
||||||
DataStore store = this.dataStoreMgr.getPrimaryDataStore(pool.getId());
|
DataStore store = this.dataStoreMgr.getPrimaryDataStore(pool.getId());
|
||||||
return store;
|
return store;
|
||||||
@ -360,14 +364,48 @@ public class SnapshotTest extends CloudStackTestNGBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//@Test
|
private VMTemplateVO createTemplateInDb() {
|
||||||
public void testCreateDataDisk() {
|
image = new VMTemplateVO();
|
||||||
DataStore primaryStore = createPrimaryDataStore();
|
image.setTemplateType(TemplateType.USER);
|
||||||
primaryStoreId = primaryStore.getId();
|
|
||||||
primaryStore = this.dataStoreMgr.getPrimaryDataStore(primaryStoreId);
|
image.setUniqueName(UUID.randomUUID().toString());
|
||||||
VolumeVO volume = createVolume(null, primaryStore.getId());
|
image.setName(UUID.randomUUID().toString());
|
||||||
VolumeInfo volInfo = this.volFactory.getVolume(volume.getId());
|
image.setPublicTemplate(true);
|
||||||
this.volumeService.createVolumeAsync(volInfo, primaryStore);
|
image.setFeatured(true);
|
||||||
|
image.setRequiresHvm(true);
|
||||||
|
image.setBits(64);
|
||||||
|
image.setFormat(Storage.ImageFormat.VHD);
|
||||||
|
image.setEnablePassword(true);
|
||||||
|
image.setEnableSshKey(true);
|
||||||
|
image.setGuestOSId(1);
|
||||||
|
image.setBootable(true);
|
||||||
|
image.setPrepopulate(true);
|
||||||
|
image.setCrossZones(true);
|
||||||
|
image.setExtractable(true);
|
||||||
|
image = imageDataDao.persist(image);
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createTemplateFromSnapshot() {
|
||||||
|
VolumeInfo vol = createCopyBaseImage();
|
||||||
|
SnapshotVO snapshotVO = createSnapshotInDb(vol);
|
||||||
|
SnapshotInfo snapshot = this.snapshotFactory.getSnapshot(snapshotVO.getId(), vol.getDataStore());
|
||||||
|
boolean result = false;
|
||||||
|
for (SnapshotStrategy strategy : this.snapshotStrategies) {
|
||||||
|
if (strategy.canHandle(snapshot)) {
|
||||||
|
snapshot = strategy.takeSnapshot(snapshot);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssertJUnit.assertTrue(result);
|
||||||
|
LocalHostEndpoint ep = new LocalHostEndpoint();
|
||||||
|
ep.setResource(new MockLocalNfsSecondaryStorageResource());
|
||||||
|
Mockito.when(epSelector.select(Mockito.any(DataObject.class), Mockito.any(DataObject.class))).thenReturn(ep);
|
||||||
|
VMTemplateVO templateVO = createTemplateInDb();
|
||||||
|
TemplateInfo tmpl = this.templateFactory.getTemplate(templateVO.getId());
|
||||||
|
DataStore imageStore = this.dataStoreMgr.getImageStore(this.dcId);
|
||||||
|
this.imageService.createTemplateFromSnapshotAsync(snapshot, tmpl, imageStore);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,7 @@ public class TemplateTest extends CloudStackTestNGBase {
|
|||||||
@Test(priority = -1)
|
@Test(priority = -1)
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
ComponentContext.initComponentsLifeCycle();
|
ComponentContext.initComponentsLifeCycle();
|
||||||
System.setProperty("paths.script", this.getScriptPath());
|
|
||||||
//create data center
|
//create data center
|
||||||
DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24",
|
DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24",
|
||||||
null, null, NetworkType.Basic, null, null, true, true, null, null);
|
null, null, NetworkType.Basic, null, null, true, true, null, null);
|
||||||
|
|||||||
@ -379,4 +379,55 @@ public class VolumeTest extends CloudStackTestNGBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VMTemplateVO createTemplateInDb() {
|
||||||
|
image = new VMTemplateVO();
|
||||||
|
image.setTemplateType(TemplateType.USER);
|
||||||
|
|
||||||
|
image.setUniqueName(UUID.randomUUID().toString());
|
||||||
|
image.setName(UUID.randomUUID().toString());
|
||||||
|
image.setPublicTemplate(true);
|
||||||
|
image.setFeatured(true);
|
||||||
|
image.setRequiresHvm(true);
|
||||||
|
image.setBits(64);
|
||||||
|
image.setFormat(Storage.ImageFormat.VHD);
|
||||||
|
image.setEnablePassword(true);
|
||||||
|
image.setEnableSshKey(true);
|
||||||
|
image.setGuestOSId(1);
|
||||||
|
image.setBootable(true);
|
||||||
|
image.setPrepopulate(true);
|
||||||
|
image.setCrossZones(true);
|
||||||
|
image.setExtractable(true);
|
||||||
|
image = imageDataDao.persist(image);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateTemplateFromVolume() {
|
||||||
|
DataStore primaryStore = createPrimaryDataStore();
|
||||||
|
primaryStoreId = primaryStore.getId();
|
||||||
|
primaryStore = this.dataStoreMgr.getPrimaryDataStore(primaryStoreId);
|
||||||
|
VolumeVO volume = createVolume(null, primaryStore.getId());
|
||||||
|
VolumeInfo volInfo = this.volFactory.getVolume(volume.getId());
|
||||||
|
AsyncCallFuture<VolumeApiResult> future = this.volumeService.createVolumeAsync(volInfo, primaryStore);
|
||||||
|
try {
|
||||||
|
VolumeApiResult result = future.get();
|
||||||
|
|
||||||
|
AssertJUnit.assertTrue(result.isSuccess());
|
||||||
|
volInfo = result.getVolume();
|
||||||
|
VMTemplateVO templateVO = createTemplateInDb();
|
||||||
|
TemplateInfo tmpl = this.templateFactory.getTemplate(templateVO.getId());
|
||||||
|
DataStore imageStore = this.dataStoreMgr.getImageStore(this.dcId);
|
||||||
|
|
||||||
|
this.imageService.createTemplateFromVolumeAsync(volInfo, tmpl, imageStore);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,6 +84,16 @@ public class DefaultEndPointSelector implements EndPointSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean moveBetweenImages(DataStore srcStore, DataStore destStore) {
|
||||||
|
DataStoreRole srcRole = srcStore.getRole();
|
||||||
|
DataStoreRole destRole = destStore.getRole();
|
||||||
|
if (srcRole == DataStoreRole.Image && destRole == DataStoreRole.Image) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@DB
|
@DB
|
||||||
protected EndPoint findEndPointInScope(Scope scope, String sqlBase) {
|
protected EndPoint findEndPointInScope(Scope scope, String sqlBase) {
|
||||||
StringBuilder sbuilder = new StringBuilder();
|
StringBuilder sbuilder = new StringBuilder();
|
||||||
@ -162,12 +172,11 @@ public class DefaultEndPointSelector implements EndPointSelector {
|
|||||||
if (moveBetweenPrimaryImage(srcStore, destStore)) {
|
if (moveBetweenPrimaryImage(srcStore, destStore)) {
|
||||||
return findEndPointForImageMove(srcStore, destStore);
|
return findEndPointForImageMove(srcStore, destStore);
|
||||||
} else if (moveBetweenCacheAndImage(srcStore, destStore)) {
|
} else if (moveBetweenCacheAndImage(srcStore, destStore)) {
|
||||||
EndPoint ep = findEndPointForImageMove(srcStore, destStore);
|
EndPoint ep = findEndpointForImageStorage(destStore);
|
||||||
if (ep == null) {
|
|
||||||
//if there is no ssvm agent running, use mgt server
|
|
||||||
ep = new LocalHostEndpoint();
|
|
||||||
}
|
|
||||||
return ep;
|
return ep;
|
||||||
|
} else if (moveBetweenImages(srcStore, destStore)) {
|
||||||
|
EndPoint ep = findEndpointForImageStorage(destStore);
|
||||||
|
return ep;
|
||||||
}
|
}
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -70,6 +70,7 @@ import com.cloud.agent.api.ManageSnapshotAnswer;
|
|||||||
import com.cloud.agent.api.ManageSnapshotCommand;
|
import com.cloud.agent.api.ManageSnapshotCommand;
|
||||||
import com.cloud.agent.api.storage.CopyVolumeAnswer;
|
import com.cloud.agent.api.storage.CopyVolumeAnswer;
|
||||||
import com.cloud.agent.api.storage.CreateAnswer;
|
import com.cloud.agent.api.storage.CreateAnswer;
|
||||||
|
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
||||||
import com.cloud.agent.api.storage.DeleteVolumeCommand;
|
import com.cloud.agent.api.storage.DeleteVolumeCommand;
|
||||||
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
|
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
|
||||||
import com.cloud.agent.api.to.DataStoreTO;
|
import com.cloud.agent.api.to.DataStoreTO;
|
||||||
@ -81,6 +82,7 @@ import com.cloud.agent.api.to.VolumeTO;
|
|||||||
import com.cloud.exception.InternalErrorException;
|
import com.cloud.exception.InternalErrorException;
|
||||||
import com.cloud.hypervisor.xen.resource.CitrixResourceBase.SRType;
|
import com.cloud.hypervisor.xen.resource.CitrixResourceBase.SRType;
|
||||||
import com.cloud.storage.DataStoreRole;
|
import com.cloud.storage.DataStoreRole;
|
||||||
|
import com.cloud.storage.Storage.ImageFormat;
|
||||||
import com.cloud.utils.S3Utils;
|
import com.cloud.utils.S3Utils;
|
||||||
import com.cloud.utils.StringUtils;
|
import com.cloud.utils.StringUtils;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
@ -1200,7 +1202,7 @@ public class XenServerStorageResource {
|
|||||||
destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid);
|
destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid);
|
||||||
|
|
||||||
SnapshotObjectTO newSnapshot = new SnapshotObjectTO();
|
SnapshotObjectTO newSnapshot = new SnapshotObjectTO();
|
||||||
newSnapshot.setPath(snapshotBackupUuid);
|
newSnapshot.setPath(folder + File.separator + snapshotBackupUuid);
|
||||||
if (fullbackup) {
|
if (fullbackup) {
|
||||||
newSnapshot.setParentSnapshotPath(null);
|
newSnapshot.setParentSnapshotPath(null);
|
||||||
} else {
|
} else {
|
||||||
@ -1218,6 +1220,76 @@ public class XenServerStorageResource {
|
|||||||
return new CopyCmdAnswer(details);
|
return new CopyCmdAnswer(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected CopyCmdAnswer createTemplateFromVolume(DataTO srcData, DataTO destData, int wait) {
|
||||||
|
Connection conn = this.hypervisorResource.getConnection();
|
||||||
|
VolumeObjectTO volume = (VolumeObjectTO)srcData;
|
||||||
|
TemplateObjectTO template = (TemplateObjectTO)destData;
|
||||||
|
NfsTO destStore = (NfsTO)destData.getDataStore();
|
||||||
|
|
||||||
|
String secondaryStoragePoolURL = destStore.getUrl();
|
||||||
|
String volumeUUID = volume.getPath();
|
||||||
|
|
||||||
|
String userSpecifiedName = template.getName();
|
||||||
|
|
||||||
|
|
||||||
|
String details = null;
|
||||||
|
SR tmpltSR = null;
|
||||||
|
boolean result = false;
|
||||||
|
String secondaryStorageMountPath = null;
|
||||||
|
String installPath = null;
|
||||||
|
try {
|
||||||
|
URI uri = new URI(secondaryStoragePoolURL);
|
||||||
|
secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
|
||||||
|
installPath = template.getPath();
|
||||||
|
if( !this.hypervisorResource.createSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath)) {
|
||||||
|
details = " Filed to create folder " + installPath + " in secondary storage";
|
||||||
|
s_logger.warn(details);
|
||||||
|
return new CopyCmdAnswer(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
VDI vol = getVDIbyUuid(conn, volumeUUID);
|
||||||
|
// create template SR
|
||||||
|
URI tmpltURI = new URI(secondaryStoragePoolURL + "/" + installPath);
|
||||||
|
tmpltSR = this.hypervisorResource.createNfsSRbyURI(conn, tmpltURI, false);
|
||||||
|
|
||||||
|
// copy volume to template SR
|
||||||
|
VDI tmpltVDI = this.hypervisorResource.cloudVDIcopy(conn, vol, tmpltSR, wait);
|
||||||
|
// scan makes XenServer pick up VDI physicalSize
|
||||||
|
tmpltSR.scan(conn);
|
||||||
|
if (userSpecifiedName != null) {
|
||||||
|
tmpltVDI.setNameLabel(conn, userSpecifiedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
String tmpltUUID = tmpltVDI.getUuid(conn);
|
||||||
|
String tmpltFilename = tmpltUUID + ".vhd";
|
||||||
|
long virtualSize = tmpltVDI.getVirtualSize(conn);
|
||||||
|
long physicalSize = tmpltVDI.getPhysicalUtilisation(conn);
|
||||||
|
// create the template.properties file
|
||||||
|
String templatePath = secondaryStorageMountPath + "/" + installPath;
|
||||||
|
result = this.hypervisorResource.postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, template.getId());
|
||||||
|
if (!result) {
|
||||||
|
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI);
|
||||||
|
}
|
||||||
|
installPath = installPath + "/" + tmpltFilename;
|
||||||
|
this.hypervisorResource.removeSR(conn, tmpltSR);
|
||||||
|
tmpltSR = null;
|
||||||
|
TemplateObjectTO newTemplate = new TemplateObjectTO();
|
||||||
|
newTemplate.setPath(installPath);
|
||||||
|
CopyCmdAnswer answer = new CopyCmdAnswer(newTemplate);
|
||||||
|
return answer;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (tmpltSR != null) {
|
||||||
|
this.hypervisorResource.removeSR(conn, tmpltSR);
|
||||||
|
}
|
||||||
|
if ( secondaryStorageMountPath != null) {
|
||||||
|
this.hypervisorResource.deleteSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath);
|
||||||
|
}
|
||||||
|
details = "Creating template from volume " + volumeUUID + " failed due to " + e.toString();
|
||||||
|
s_logger.error(details, e);
|
||||||
|
}
|
||||||
|
return new CopyCmdAnswer(details);
|
||||||
|
}
|
||||||
|
|
||||||
protected Answer execute(CopyCommand cmd) {
|
protected Answer execute(CopyCommand cmd) {
|
||||||
DataTO srcData = cmd.getSrcTO();
|
DataTO srcData = cmd.getSrcTO();
|
||||||
DataTO destData = cmd.getDestTO();
|
DataTO destData = cmd.getDestTO();
|
||||||
@ -1235,11 +1307,15 @@ public class XenServerStorageResource {
|
|||||||
} else if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcDataStore.getRole() == DataStoreRole.Primary && destDataStore.getRole() == DataStoreRole.Primary) {
|
} else if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcDataStore.getRole() == DataStoreRole.Primary && destDataStore.getRole() == DataStoreRole.Primary) {
|
||||||
//clone template to a volume
|
//clone template to a volume
|
||||||
return cloneVolumeFromBaseTemplate(srcData, destData);
|
return cloneVolumeFromBaseTemplate(srcData, destData);
|
||||||
} else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.ImageCache) {
|
} else if (srcData.getObjectType() == DataObjectType.VOLUME && (srcData.getDataStore().getRole() == DataStoreRole.ImageCache || srcDataStore.getRole() == DataStoreRole.Image)) {
|
||||||
//copy volume from image cache to primary
|
//copy volume from image cache to primary
|
||||||
return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait());
|
return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait());
|
||||||
} else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
|
} else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
|
||||||
return copyVolumeFromPrimaryToSecondary(srcData, destData, cmd.getWait());
|
if (destData.getObjectType() == DataObjectType.VOLUME) {
|
||||||
|
return copyVolumeFromPrimaryToSecondary(srcData, destData, cmd.getWait());
|
||||||
|
} else if (destData.getObjectType() == DataObjectType.TEMPLATE) {
|
||||||
|
return createTemplateFromVolume(srcData, destData, cmd.getWait());
|
||||||
|
}
|
||||||
} else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
|
} else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
|
||||||
DataTO cacheData = cmd.getCacheTO();
|
DataTO cacheData = cmd.getCacheTO();
|
||||||
return backupSnasphot(srcData, destData, cacheData, cmd.getWait());
|
return backupSnasphot(srcData, destData, cacheData, cmd.getWait());
|
||||||
|
|||||||
98
scripts/storage/secondary/create_privatetemplate_from_snapshot_xen.sh
Executable file
98
scripts/storage/secondary/create_privatetemplate_from_snapshot_xen.sh
Executable file
@ -0,0 +1,98 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
#set -x
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
printf "Usage: %s -t [template path] -n [template name] -s [snapshot name] -p [snapshot path] \n" $(basename $0)
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshotPath=
|
||||||
|
snapshotName=
|
||||||
|
templatePath=
|
||||||
|
templateName=
|
||||||
|
while getopts ':s:n:t:p:' OPTION
|
||||||
|
do
|
||||||
|
case $OPTION in
|
||||||
|
t) tflag=1
|
||||||
|
templatePath="$OPTARG"
|
||||||
|
;;
|
||||||
|
n) nflag=1
|
||||||
|
templateName="$OPTARG"
|
||||||
|
;;
|
||||||
|
s) sflag=1
|
||||||
|
snapshotName="$OPTARG"
|
||||||
|
;;
|
||||||
|
p) pflag=1
|
||||||
|
snapshotPath="$OPTARG"
|
||||||
|
;;
|
||||||
|
?) usage
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$sflag$nflag$tflag$pflag" != "1111" ]
|
||||||
|
then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VHDUTIL="/bin/vhd-util"
|
||||||
|
desvhd=$templatePath/$templateName
|
||||||
|
srcvhd=$snapshotPath/$snapshotName
|
||||||
|
|
||||||
|
copyvhd()
|
||||||
|
{
|
||||||
|
local desvhd=$1
|
||||||
|
local srcvhd=$2
|
||||||
|
local parent=
|
||||||
|
parent=`$VHDUTIL query -p -n $srcvhd`
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "30#failed to query $srcvhd"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ "${parent}" =~ " no parent" ]]; then
|
||||||
|
dd if=$srcvhd of=$desvhd bs=2M
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "31#failed to dd $srcvhd to $desvhd"
|
||||||
|
rm -rf $desvhd > /dev/null
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
copyvhd $desvhd $parent
|
||||||
|
$VHDUTIL coalesce -p $desvhd -n $srcvhd
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "32#failed to coalesce $desvhd to $srcvhd"
|
||||||
|
rm -rf $desvhd > /dev/null
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
copyvhd $desvhd $srcvhd
|
||||||
|
imgsize=$(ls -l $desvhd| awk -F" " '{print $5}')
|
||||||
|
propertyFile=/$templatePath/template.properties
|
||||||
|
touch $propertyFile
|
||||||
|
echo -n "" > $propertyFile
|
||||||
|
|
||||||
|
echo "filename=$templateName" > $propertyFile
|
||||||
|
echo "hvm=$hvm" >> $propertyFile
|
||||||
|
echo "size=$imgsize" >> $propertyFile
|
||||||
|
|
||||||
|
exit 0
|
||||||
Loading…
x
Reference in New Issue
Block a user