From 5aeca646ae18f620ea41ecbd8c1128aa1d1f30b2 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 7 May 2013 17:46:10 -0700 Subject: [PATCH] make create template from volume/snapshot work --- .../resource/NfsSecondaryStorageResource.java | 118 +++++++++++++++--- .../storage/to/SnapshotObjectTO.java | 11 ++ .../motion/AncientDataMotionStrategy.java | 2 +- .../storage/test/CloudStackTestNGBase.java | 3 + .../cloudstack/storage/test/SnapshotTest.java | 62 +++++++-- .../cloudstack/storage/test/TemplateTest.java | 2 +- .../cloudstack/storage/test/VolumeTest.java | 51 ++++++++ .../endpoint/DefaultEndPointSelector.java | 19 ++- .../resource/XenServerStorageResource.java | 82 +++++++++++- ...reate_privatetemplate_from_snapshot_xen.sh | 98 +++++++++++++++ 10 files changed, 407 insertions(+), 41 deletions(-) create mode 100755 scripts/storage/secondary/create_privatetemplate_from_snapshot_xen.sh diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java index 48854dfbcb5..e9ec8391910 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java @@ -53,6 +53,7 @@ import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.DownloadCommand; import org.apache.cloudstack.storage.command.DownloadProgressCommand; 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.commons.lang.StringUtils; import org.apache.log4j.Logger; @@ -103,6 +104,7 @@ import com.cloud.agent.api.to.SwiftTO; import com.cloud.exception.InternalErrorException; import com.cloud.host.Host; import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.DataStoreRole; 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.DownloadManagerImpl; 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.TemplateProp; import com.cloud.storage.template.UploadManager; import com.cloud.storage.template.UploadManagerImpl; +import com.cloud.storage.template.VhdProcessor; import com.cloud.utils.NumbersUtil; import com.cloud.utils.S3Utils; 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 _tmpltDir = "/var/cloudstack/template"; final private String _tmpltpp = "template.properties"; + private String createTemplateFromSnapshotXenScript; @Override public void disconnected() { @@ -280,11 +287,90 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S return new CopyCmdAnswer(errMsg); } } + + + protected Answer copySnapshotToTemplateFromNfsToNfsXenserver(CopyCommand cmd, SnapshotObjectTO srcData, NfsTO srcDataStore, TemplateObjectTO destData, NfsTO destDataStore) { + 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(); - protected Answer copyFromSwiftToNfs(CopyCommand cmd, DataTO srcData, SwiftTO srcImageStore, + String errMsg = null; + try { + this._storage.mkdir(destPath); - DataTO destData, NfsTO destImageStore) { - return Answer.createUnsupportedCommandAnswer(cmd); + 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 params = new HashMap(); + 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) { @@ -292,23 +378,12 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S DataTO destData = cmd.getDestTO(); DataStoreTO srcDataStore = srcData.getDataStore(); DataStoreTO destDataStore = destData.getDataStore(); - - if (srcDataStore.getRole() == DataStoreRole.Image && destDataStore.getRole() == DataStoreRole.ImageCache) { - - 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); - } - + + if (srcData.getObjectType() == DataObjectType.SNAPSHOT && destData.getObjectType() == DataObjectType.TEMPLATE) { + return createTemplateFromSnapshot(cmd); } + + return Answer.createUnsupportedCommandAnswer(cmd); } @@ -1652,6 +1727,11 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S if (_configIpFirewallScr != null) { 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"); if (_role == null) diff --git a/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index 6d98045cc7a..ed4cbe1756a 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -5,6 +5,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; public class SnapshotObjectTO implements DataTO { private String path; @@ -13,6 +14,7 @@ public class SnapshotObjectTO implements DataTO { private DataStoreTO dataStore; private String vmName; private String name; + private HypervisorType hypervisorType; private long id; public SnapshotObjectTO() { @@ -29,6 +31,7 @@ public class SnapshotObjectTO implements DataTO { } this.dataStore = snapshot.getDataStore().getTO(); this.setName(snapshot.getName()); + this.hypervisorType = snapshot.getHypervisorType(); } @Override @@ -89,4 +92,12 @@ public class SnapshotObjectTO implements DataTO { public void setName(String name) { this.name = name; } + + public HypervisorType getHypervisorType() { + return hypervisorType; + } + + public void setHypervisorType(HypervisorType hypervisorType) { + this.hypervisorType = hypervisorType; + } } diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index db3971c673e..560c34f0a3f 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -327,7 +327,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { .parseInt(Config.CreatePrivateTemplateFromSnapshotWait .getDefaultValue())); - if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) { + if (needCacheStorage(srcData, destData)) { SnapshotInfo snapshot = (SnapshotInfo)srcData; srcData = cacheSnapshotChain(snapshot); } diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java index 8bf14f60eac..77b6ec302cc 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java @@ -94,6 +94,9 @@ public class CloudStackTestNGBase extends AbstractTestNGSpringContextTests { this.s3TemplateBucket = s3_template_bucket; this.s3UseHttps = Boolean.parseBoolean(s3_usehttps); this.scriptPath = scriptPath; + if (this.scriptPath != null) { + System.setProperty("paths.script", this.getScriptPath()); + } } protected String getHostGuid() { diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java index 203f762a0e4..9dc68546005 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java @@ -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.DataStore; 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.EndPointSelector; 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.type.RootDisk; 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.command.CopyCmdAnswer; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; @@ -144,6 +147,7 @@ public class SnapshotTest extends CloudStackTestNGBase { SnapshotDao snapshotDao; @Inject EndPointSelector epSelector; + long primaryStoreId; VMTemplateVO image; String imageStoreName = "testImageStore"; @@ -196,7 +200,7 @@ public class SnapshotTest extends CloudStackTestNGBase { imageStore = new ImageStoreVO(); imageStore.setName(imageStoreName); imageStore.setDataCenterId(dcId); - imageStore.setProviderName("CloudStack ImageStore Provider"); + imageStore.setProviderName(DataStoreProvider.NFS_IMAGE); imageStore.setRole(DataStoreRole.Image); imageStore.setUrl(this.getSecondaryStorage()); imageStore.setUuid(UUID.randomUUID().toString()); @@ -301,7 +305,7 @@ public class SnapshotTest extends CloudStackTestNGBase { pool.setPoolType(StoragePoolType.NetworkFilesystem); pool.setPodId(podId); pool.setScope(ScopeType.CLUSTER); - pool.setStorageProviderName("cloudstack primary data store provider"); + pool.setStorageProviderName(DataStoreProvider.DEFAULT_PRIMARY); pool = this.primaryStoreDao.persist(pool); DataStore store = this.dataStoreMgr.getPrimaryDataStore(pool.getId()); return store; @@ -359,15 +363,49 @@ public class SnapshotTest extends CloudStackTestNGBase { } } } - - //@Test - public void testCreateDataDisk() { - DataStore primaryStore = createPrimaryDataStore(); - primaryStoreId = primaryStore.getId(); - primaryStore = this.dataStoreMgr.getPrimaryDataStore(primaryStoreId); - VolumeVO volume = createVolume(null, primaryStore.getId()); - VolumeInfo volInfo = this.volFactory.getVolume(volume.getId()); - this.volumeService.createVolumeAsync(volInfo, primaryStore); + + 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 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); } - } diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TemplateTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TemplateTest.java index 3ef1b542743..294d4b5a2c2 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TemplateTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TemplateTest.java @@ -68,7 +68,7 @@ public class TemplateTest extends CloudStackTestNGBase { @Test(priority = -1) public void setUp() { ComponentContext.initComponentsLifeCycle(); - System.setProperty("paths.script", this.getScriptPath()); + //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", null, null, NetworkType.Basic, null, null, true, true, null, null); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java index 01715451f33..a35495999d4 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java @@ -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 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(); + } + + + } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java index 5b223010d9b..54059f28a59 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java +++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java @@ -83,6 +83,16 @@ public class DefaultEndPointSelector implements EndPointSelector { return false; } } + + 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 protected EndPoint findEndPointInScope(Scope scope, String sqlBase) { @@ -162,12 +172,11 @@ public class DefaultEndPointSelector implements EndPointSelector { if (moveBetweenPrimaryImage(srcStore, destStore)) { return findEndPointForImageMove(srcStore, destStore); } else if (moveBetweenCacheAndImage(srcStore, destStore)) { - EndPoint ep = findEndPointForImageMove(srcStore, destStore); - if (ep == null) { - //if there is no ssvm agent running, use mgt server - ep = new LocalHostEndpoint(); - } + EndPoint ep = findEndpointForImageStorage(destStore); return ep; + } else if (moveBetweenImages(srcStore, destStore)) { + EndPoint ep = findEndpointForImageStorage(destStore); + return ep; } // TODO Auto-generated method stub return null; diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java index ab01c7262e8..fc3a0dde3a6 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java @@ -70,6 +70,7 @@ import com.cloud.agent.api.ManageSnapshotAnswer; import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; 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.PrimaryStorageDownloadAnswer; 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.hypervisor.xen.resource.CitrixResourceBase.SRType; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Storage.ImageFormat; import com.cloud.utils.S3Utils; import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; @@ -1200,7 +1202,7 @@ public class XenServerStorageResource { destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid); SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); - newSnapshot.setPath(snapshotBackupUuid); + newSnapshot.setPath(folder + File.separator + snapshotBackupUuid); if (fullbackup) { newSnapshot.setParentSnapshotPath(null); } else { @@ -1217,6 +1219,76 @@ public class XenServerStorageResource { 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) { DataTO srcData = cmd.getSrcTO(); @@ -1235,11 +1307,15 @@ public class XenServerStorageResource { } else if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcDataStore.getRole() == DataStoreRole.Primary && destDataStore.getRole() == DataStoreRole.Primary) { //clone template to a volume 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 return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait()); } 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) { DataTO cacheData = cmd.getCacheTO(); return backupSnasphot(srcData, destData, cacheData, cmd.getWait()); diff --git a/scripts/storage/secondary/create_privatetemplate_from_snapshot_xen.sh b/scripts/storage/secondary/create_privatetemplate_from_snapshot_xen.sh new file mode 100755 index 00000000000..309bcbdb683 --- /dev/null +++ b/scripts/storage/secondary/create_privatetemplate_from_snapshot_xen.sh @@ -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