You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by li...@apache.org on 2014/05/02 14:39:30 UTC

[01/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Repository: cloudstack
Updated Branches:
  refs/heads/multiple-disk-ova [created] 3532ffe5a


Read OVF template to extract volumes and meta data.

Signed-off-by: Sateesh Chodapuneedi <sa...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/11c011dc
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/11c011dc
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/11c011dc

Branch: refs/heads/multiple-disk-ova
Commit: 11c011dc10efdc392f0bb2531c940f6866d169cc
Parents: 155745e
Author: Sateesh Chodapuneedi <sa...@apache.org>
Authored: Wed Mar 12 11:56:56 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 13:06:44 2014 +0530

----------------------------------------------------------------------
 .../vmware/mo/HypervisorHostHelper.java         | 81 ++++++++++++++++++++
 1 file changed, 81 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/11c011dc/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index 38b68b3..8e2254e 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 import org.apache.log4j.Logger;
 
@@ -56,12 +57,15 @@ import com.vmware.vim25.VMwareDVSConfigSpec;
 import com.vmware.vim25.VMwareDVSPortSetting;
 import com.vmware.vim25.VMwareDVSPvlanConfigSpec;
 import com.vmware.vim25.VMwareDVSPvlanMapEntry;
+import com.vmware.vim25.VirtualDevice;
 import com.vmware.vim25.VirtualDeviceConfigSpec;
 import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
+import com.vmware.vim25.VirtualDisk;
 import com.vmware.vim25.VirtualLsiLogicController;
 import com.vmware.vim25.VirtualMachineConfigSpec;
 import com.vmware.vim25.VirtualMachineFileInfo;
 import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
+import com.vmware.vim25.VirtualMachineImportSpec;
 import com.vmware.vim25.VirtualMachineVideoCard;
 import com.vmware.vim25.VirtualSCSISharing;
 import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
@@ -74,6 +78,7 @@ import com.cloud.hypervisor.vmware.util.VmwareHelper;
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.utils.ActionDelegate;
 import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
 import com.cloud.utils.cisco.n1kv.vsm.NetconfHelper;
 import com.cloud.utils.cisco.n1kv.vsm.PolicyMap;
 import com.cloud.utils.cisco.n1kv.vsm.PortProfile;
@@ -92,6 +97,7 @@ public class HypervisorHostHelper {
 
     // make vmware-base loosely coupled with cloud-specific stuff, duplicate VLAN.UNTAGGED constant here
     private static final String UNTAGGED_VLAN_NAME = "untagged";
+    private static final String VMDK_PACK_DIR = "ova";
 
     public static VirtualMachineMO findVmFromObjectContent(VmwareContext context, ObjectContent[] ocs, String name, String instanceNameCustomField) {
 
@@ -1174,6 +1180,81 @@ public class HypervisorHostHelper {
         return false;
     }
 
+    public static List<Ternary<String, Long, Boolean>> readOVF(VmwareHypervisorHost host, String ovfFilePath, DatastoreMO dsMo, ManagedObjectReference morRp,
+            ManagedObjectReference morHost) throws Exception {
+
+        assert (morRp != null);
+
+        String importEntityName = UUID.randomUUID().toString();
+        OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams();
+        importSpecParams.setHostSystem(morHost);
+        importSpecParams.setLocale("US");
+        importSpecParams.setEntityName(importEntityName);
+        importSpecParams.setDeploymentOption("");
+
+        String ovfDescriptor = HttpNfcLeaseMO.readOvfContent(ovfFilePath);
+        VmwareContext context = host.getContext();
+        OvfCreateImportSpecResult ovfImportResult =
+                context.getService().createImportSpec(context.getServiceContent().getOvfManager(), ovfDescriptor, morRp, dsMo.getMor(), importSpecParams);
+
+        if (ovfImportResult == null) {
+            String msg = "createImportSpec() failed. ovfFilePath: " + ovfFilePath;
+            s_logger.error(msg);
+            throw new Exception(msg);
+        }
+
+        if(!ovfImportResult.getError().isEmpty()) {
+            for (LocalizedMethodFault fault : ovfImportResult.getError()) {
+                s_logger.error("createImportSpec error: " + fault.getLocalizedMessage());
+            }
+            throw new CloudException("Failed to create an import spec from " + ovfFilePath + ". Check log for details.");
+        }
+
+        if (!ovfImportResult.getWarning().isEmpty()) {
+            for (LocalizedMethodFault fault : ovfImportResult.getError()) {
+                s_logger.warn("createImportSpec warning: " + fault.getLocalizedMessage());
+            }
+        }
+
+        VirtualMachineImportSpec importSpec = new VirtualMachineImportSpec();
+        importSpec = (VirtualMachineImportSpec)ovfImportResult.getImportSpec();
+        if (importSpec == null) {
+            String msg = "createImportSpec() failed to create import specification for OVF template at " + ovfFilePath;
+            s_logger.error(msg);
+            throw new Exception(msg);
+        }
+
+        File ovfFile = new File(ovfFilePath);
+        int diskCount = 0;
+        long sizeKb = 0;
+        List<Ternary<String, Long, Boolean>> ovfVolumeInfos = new ArrayList<Ternary<String, Long, Boolean>>();
+        Ternary<String, Long, Boolean> ovfVolumeInfo = null;
+        List<String> files = new ArrayList<String>();
+        String absFile = null;
+
+        for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem()) {
+            absFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath();
+            files.add(absFile);
+        }
+
+        boolean lookForFirstDisk = true;
+        Boolean osDisk = true;
+        List<VirtualDeviceConfigSpec> deviceConfigList = importSpec.getConfigSpec().getDeviceChange();
+        for (VirtualDeviceConfigSpec deviceSpec : deviceConfigList) {
+            VirtualDevice device = deviceSpec.getDevice();
+            if (device instanceof VirtualDisk) {
+                sizeKb = ((VirtualDisk)device).getCapacityInKB();
+                if (lookForFirstDisk && diskCount == 0) {
+                    osDisk = true;
+                    diskCount++;
+                }
+                ovfVolumeInfo = new Ternary<String, Long, Boolean>(files.get(diskCount), sizeKb, osDisk);
+                ovfVolumeInfos.add(ovfVolumeInfo);
+            }
+        }
+        return ovfVolumeInfos;
+    }
+
     public static VirtualMachineMO createWorkerVM(VmwareHypervisorHost hyperHost, DatastoreMO dsMo, String vmName) throws Exception {
 
         // Allow worker VM to float within cluster so that we will have better chance to


[06/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757 Support OVA with multiple volumes

Update capacity of vmdk file while attaching to disk to worker vm.

Signed-off-by: Sateesh Chodapuneedi <sa...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/95cffaa9
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/95cffaa9
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/95cffaa9

Branch: refs/heads/multiple-disk-ova
Commit: 95cffaa9df7e637f1e61d9eabcab05322fc8108f
Parents: e467d11
Author: Sateesh Chodapuneedi <sa...@apache.org>
Authored: Thu Mar 13 13:01:43 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 13:07:51 2014 +0530

----------------------------------------------------------------------
 .../src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/95cffaa9/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index e17b305..2a8474b 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -1350,7 +1350,8 @@ public class HypervisorHostHelper {
             VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
             VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
 
-            VirtualDevice device = VmwareHelper.prepareDiskDevice(workerVmMo, null, -1, disks, morDs, -1, 1);
+            VirtualDisk device = VmwareHelper.prepareDiskDevice(workerVmMo, null, -1, disks, morDs, -1, 1);
+            device.setCapacityInKB(size);
 
             deviceConfigSpec.setDevice(device);
             deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);


[11/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. Template copy - Copy all Datadisk templates that belong to the template along with the template.
Disallow individual copy of Datadisk templates


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/6a384415
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/6a384415
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/6a384415

Branch: refs/heads/multiple-disk-ova
Commit: 6a3844155d405e750af0d7b74d43ffcbb5b8e605
Parents: 07d514f
Author: Likitha Shetty <li...@citrix.com>
Authored: Tue Apr 29 17:48:34 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:52:12 2014 +0530

----------------------------------------------------------------------
 .../com/cloud/template/TemplateManagerImpl.java | 25 ++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a384415/server/src/com/cloud/template/TemplateManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index 1268f24..8083be7 100755
--- a/server/src/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/com/cloud/template/TemplateManagerImpl.java
@@ -656,12 +656,33 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
                     UsageEventUtils.publishUsageEvent(copyEventType, account.getId(), dstZoneId, tmpltId, null, null, null, srcTmpltStore.getPhysicalSize(),
                             srcTmpltStore.getSize(), template.getClass().getName(), template.getUuid());
                 }
-                return true;
+                // Copy every Datadisk template that belongs to the template to Destination zone
+                List<VMTemplateVO> dataDiskTemplates = _tmpltDao.listByParentTemplatetId(template.getId());
+                if (dataDiskTemplates != null && !dataDiskTemplates.isEmpty()) {
+                    for (VMTemplateVO dataDiskTemplate : dataDiskTemplates) {
+                        s_logger.debug("Copying " + dataDiskTemplates.size() + " for source template " + template.getId() + ". Copy all Datadisk templates to destination datastore " + dstSecStore.getName());
+                        TemplateInfo srcDataDiskTemplate = _tmplFactory.getTemplate(dataDiskTemplate.getId(), srcSecStore);
+                        AsyncCallFuture<TemplateApiResult> dataDiskCopyFuture = _tmpltSvr.copyTemplate(srcDataDiskTemplate, dstSecStore);
+                        try {
+                            TemplateApiResult dataDiskCopyResult = dataDiskCopyFuture.get();
+                            if (dataDiskCopyResult.isFailed()) {
+                                s_logger.error("Copy of datadisk template: " + srcDataDiskTemplate.getId() + " to image store: " + dstSecStore.getName()
+                                        + " failed with error: " + dataDiskCopyResult.getResult() + " , will try copying the next one");
+                                continue; // Continue to copy next Datadisk template
+                            }
+                            _tmpltDao.addTemplateToZone(dataDiskTemplate, dstZoneId);
+                            _resourceLimitMgr.incrementResourceCount(dataDiskTemplate.getAccountId(), ResourceType.secondary_storage, dataDiskTemplate.getSize());
+                        } catch (Exception ex) {
+                            s_logger.error("Failed to copy datadisk template: " + srcDataDiskTemplate.getId() + " to image store: " + dstSecStore.getName()
+                                    + " , will try copying the next one");
+                        }
+                    }
+                }
             } catch (Exception ex) {
                 s_logger.debug("failed to copy template to image store:" + dstSecStore.getName() + " ,will try next one");
             }
         }
-        return false;
+        return true;
 
     }
 


[07/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. During template registration, after template download analyze OVA template to identify additional disks and create Datadisk templates for each of the additional disks.


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/902f231e
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/902f231e
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/902f231e

Branch: refs/heads/multiple-disk-ova
Commit: 902f231e071e9f8410036022ef6281cc9872eea5
Parents: 95cffaa
Author: Likitha Shetty <li...@citrix.com>
Authored: Tue Mar 25 11:13:47 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:51:07 2014 +0530

----------------------------------------------------------------------
 .../cloud/agent/api/to/DatadiskTemplateTO.java  |  74 +++++++++
 api/src/com/cloud/storage/Storage.java          |   3 +-
 .../cloud/template/VirtualMachineTemplate.java  |   2 +
 .../storage/CreateDatadiskTemplateAnswer.java   |  38 +++++
 .../storage/CreateDatadiskTemplateCommand.java  |  54 +++++++
 .../agent/api/storage/GetDatadisksAnswer.java   |  40 +++++
 .../agent/api/storage/GetDatadisksCommand.java  |  43 +++++
 .../cloud/storage/template/OVAProcessor.java    |  48 ++++++
 .../cloudstack/storage/to/TemplateObjectTO.java |  21 +++
 .../subsystem/api/storage/EndPointSelector.java |   4 +-
 .../subsystem/api/storage/TemplateService.java  |   7 +
 .../image/datastore/ImageStoreEntity.java       |   8 +
 .../src/com/cloud/storage/VMTemplateVO.java     |  12 ++
 .../storage/image/TemplateServiceImpl.java      |  73 +++++++++
 .../storage/image/store/ImageStoreImpl.java     |  14 ++
 .../storage/image/store/TemplateObject.java     |  11 ++
 .../endpoint/DefaultEndPointSelector.java       |  13 +-
 .../storage/image/BaseImageStoreDriverImpl.java |  74 ++++++++-
 .../storage/image/ImageStoreDriver.java         |  10 ++
 .../vmware/manager/VmwareStorageManager.java    |   6 +
 .../manager/VmwareStorageManagerImpl.java       | 159 +++++++++++++++++++
 .../vmware/resource/VmwareResource.java         |  46 ++++++
 .../VmwareSecondaryStorageResourceHandler.java  |  14 ++
 .../src/com/cloud/hypervisor/XenServerGuru.java |   2 +-
 .../template/HypervisorTemplateAdapter.java     |  66 ++++++++
 .../com/cloud/template/TemplateManagerImpl.java |   5 +
 setup/db/db/schema-440to450.sql                 |   2 +
 .../vmware/mo/HypervisorHostHelper.java         |  79 ++++-----
 28 files changed, 882 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/api/src/com/cloud/agent/api/to/DatadiskTemplateTO.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/to/DatadiskTemplateTO.java b/api/src/com/cloud/agent/api/to/DatadiskTemplateTO.java
new file mode 100644
index 0000000..34d7237
--- /dev/null
+++ b/api/src/com/cloud/agent/api/to/DatadiskTemplateTO.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package com.cloud.agent.api.to;
+
+public class DatadiskTemplateTO {
+    private long id;
+    private String uniqueName;
+    private String path;
+    private Long virtualSize;
+    private Long fileSize;
+
+    public DatadiskTemplateTO() {
+    }
+
+    public DatadiskTemplateTO(long id, String uniqueName, String path, Long virtualSize, Long fileSize) {
+        this.id = id;
+        this.uniqueName = uniqueName;
+        this.path = path;
+        this.virtualSize = virtualSize;
+        this.fileSize = fileSize;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getUniqueName() {
+        return uniqueName;
+    }
+
+    public void setUniqueName(String uniqueName) {
+        this.uniqueName = uniqueName;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public Long getVirtualSize() {
+        return virtualSize;
+    }
+
+    public void setVirtualSize(Long virtualSize) {
+        this.virtualSize = virtualSize;
+    }
+
+    public Long getFileSize() {
+        return fileSize;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/api/src/com/cloud/storage/Storage.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/storage/Storage.java b/api/src/com/cloud/storage/Storage.java
index cc8d010..69aadc9 100755
--- a/api/src/com/cloud/storage/Storage.java
+++ b/api/src/com/cloud/storage/Storage.java
@@ -112,7 +112,8 @@ public class Storage {
         SYSTEM, /* routing, system vm template */
         BUILTIN, /* buildin template */
         PERHOST, /* every host has this template, don't need to install it in secondary storage */
-        USER /* User supplied template/iso */
+        USER, /* User supplied template/iso */
+        DATADISK /* Template corresponding to a datadisk(non root disk) present in an OVA */
     }
 
     public static enum StoragePoolType {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/api/src/com/cloud/template/VirtualMachineTemplate.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/template/VirtualMachineTemplate.java b/api/src/com/cloud/template/VirtualMachineTemplate.java
index 599212b..832cd6c 100755
--- a/api/src/com/cloud/template/VirtualMachineTemplate.java
+++ b/api/src/com/cloud/template/VirtualMachineTemplate.java
@@ -100,4 +100,6 @@ public interface VirtualMachineTemplate extends ControlledEntity, Identity, Inte
     Map getDetails();
 
     boolean isDynamicallyScalable();
+
+    Long getParentTemplateId();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateAnswer.java b/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateAnswer.java
new file mode 100644
index 0000000..58e8335
--- /dev/null
+++ b/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateAnswer.java
@@ -0,0 +1,38 @@
+// 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.
+package com.cloud.agent.api.storage;
+
+import org.apache.cloudstack.storage.to.TemplateObjectTO;
+
+import com.cloud.agent.api.Answer;
+
+public class CreateDatadiskTemplateAnswer extends Answer {
+    private TemplateObjectTO dataDiskTemplate = null;
+
+    public CreateDatadiskTemplateAnswer(TemplateObjectTO dataDiskTemplate) {
+        super(null);
+        this.dataDiskTemplate = dataDiskTemplate;
+    }
+
+    public TemplateObjectTO getDataDiskTemplate() {
+        return dataDiskTemplate;
+    }
+
+    public CreateDatadiskTemplateAnswer(String errMsg) {
+        super(null, false, errMsg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateCommand.java b/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateCommand.java
new file mode 100644
index 0000000..bd3843b
--- /dev/null
+++ b/core/src/com/cloud/agent/api/storage/CreateDatadiskTemplateCommand.java
@@ -0,0 +1,54 @@
+// 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.
+package com.cloud.agent.api.storage;
+
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.to.DataTO;
+
+public final class CreateDatadiskTemplateCommand extends Command {
+    private DataTO dataDiskTemplate;
+    private String path;
+    private long fileSize;
+
+    public CreateDatadiskTemplateCommand(DataTO dataDiskTemplate, String path, long fileSize) {
+        super();
+        this.dataDiskTemplate = dataDiskTemplate;
+        this.path = path;
+        this.fileSize = fileSize;
+    }
+
+    protected CreateDatadiskTemplateCommand() {
+        super();
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+
+    public DataTO getDataDiskTemplate() {
+        return dataDiskTemplate;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public long getFileSize() {
+        return fileSize;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/core/src/com/cloud/agent/api/storage/GetDatadisksAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/storage/GetDatadisksAnswer.java b/core/src/com/cloud/agent/api/storage/GetDatadisksAnswer.java
new file mode 100644
index 0000000..ffcf26f
--- /dev/null
+++ b/core/src/com/cloud/agent/api/storage/GetDatadisksAnswer.java
@@ -0,0 +1,40 @@
+// 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.
+package com.cloud.agent.api.storage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.utils.Ternary;
+
+public class GetDatadisksAnswer extends Answer {
+    List<Ternary<String, Long, Long>> dataDiskDetails = new ArrayList<Ternary<String, Long, Long>>();
+
+    public GetDatadisksAnswer(List<Ternary<String, Long, Long>> dataDiskDetails) {
+        super(null);
+        this.dataDiskDetails = dataDiskDetails;
+    }
+
+    public List<Ternary<String, Long, Long>> getDataDiskDetails() {
+        return dataDiskDetails;
+    }
+
+    public GetDatadisksAnswer(String errMsg) {
+        super(null, false, errMsg);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/core/src/com/cloud/agent/api/storage/GetDatadisksCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/storage/GetDatadisksCommand.java b/core/src/com/cloud/agent/api/storage/GetDatadisksCommand.java
new file mode 100644
index 0000000..ce0fb1c
--- /dev/null
+++ b/core/src/com/cloud/agent/api/storage/GetDatadisksCommand.java
@@ -0,0 +1,43 @@
+// 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.
+package com.cloud.agent.api.storage;
+
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.to.DataTO;
+
+public final class GetDatadisksCommand extends Command {
+    private DataTO data;
+
+    public GetDatadisksCommand(DataTO data) {
+        super();
+        this.data = data;
+    }
+
+    protected GetDatadisksCommand() {
+        super();
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+
+    public DataTO getData() {
+        return data;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/core/src/com/cloud/storage/template/OVAProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/storage/template/OVAProcessor.java b/core/src/com/cloud/storage/template/OVAProcessor.java
index 0db3bb0..7f8b7f9 100644
--- a/core/src/com/cloud/storage/template/OVAProcessor.java
+++ b/core/src/com/cloud/storage/template/OVAProcessor.java
@@ -26,10 +26,12 @@ import javax.xml.parsers.DocumentBuilderFactory;
 import org.apache.log4j.Logger;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
 
 import com.cloud.exception.InternalErrorException;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.StorageLayer;
+import com.cloud.utils.Pair;
 import com.cloud.utils.component.AdapterBase;
 import com.cloud.utils.script.Script;
 
@@ -131,6 +133,52 @@ public class OVAProcessor extends AdapterBase implements Processor {
         }
     }
 
+    public Pair<Long, Long> getDiskDetails(String ovfFilePath, String diskName) throws InternalErrorException {
+        long virtualSize = 0;
+        long fileSize = 0;
+        String fileId = null;
+        try {
+            Document ovfDoc = null;
+            ovfDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(ovfFilePath));
+            NodeList disks = ovfDoc.getElementsByTagName("Disk");
+            NodeList files = ovfDoc.getElementsByTagName("File");
+            for (int j = 0; j < files.getLength(); j++) {
+                Element file = (Element)files.item(j);
+                if (file.getAttribute("ovf:href").equals(diskName)) {
+                    fileSize = Long.parseLong(file.getAttribute("ovf:size"));
+                    fileId = file.getAttribute("ovf:id");
+                    break;
+                }
+            }
+            for (int i = 0; i < disks.getLength(); i++) {
+                Element disk = (Element)disks.item(i);
+                if (disk.getAttribute("ovf:fileRef").equals(fileId)) {
+                    virtualSize = Long.parseLong(disk.getAttribute("ovf:capacity"));
+                    String allocationUnits = disk.getAttribute("ovf:capacityAllocationUnits");
+                    if ((virtualSize != 0) && (allocationUnits != null)) {
+                        long units = 1;
+                        if (allocationUnits.equalsIgnoreCase("KB") || allocationUnits.equalsIgnoreCase("KiloBytes") || allocationUnits.equalsIgnoreCase("byte * 2^10")) {
+                            units = 1024;
+                        } else if (allocationUnits.equalsIgnoreCase("MB") || allocationUnits.equalsIgnoreCase("MegaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^20")) {
+                            units = 1024 * 1024;
+                        } else if (allocationUnits.equalsIgnoreCase("GB") || allocationUnits.equalsIgnoreCase("GigaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^30")) {
+                            units = 1024 * 1024 * 1024;
+                        }
+                        virtualSize = virtualSize * units;
+                    } else {
+                        throw new InternalErrorException("Failed to read capacity and capacityAllocationUnits from the OVF file: " + ovfFilePath);
+                    }
+                    break;
+                }
+            }
+            return new Pair<Long, Long>(virtualSize, fileSize);
+        } catch (Exception e) {
+            String msg = "Unable to parse OVF XML document to get the virtual disk size due to" + e;
+            s_logger.error(msg);
+            throw new InternalErrorException(msg);
+        }
+    }
+
     private String getOVFFilePath(String srcOVAFileName) {
         File file = new File(srcOVAFileName);
         assert (_storage != null);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java b/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
index b201c38..55dbcc5 100644
--- a/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
+++ b/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
@@ -41,6 +41,8 @@ public class TemplateObjectTO implements DataTO {
     private Long size;
     private Long physicalSize;
     private Hypervisor.HypervisorType hypervisorType;
+    private boolean bootable;
+    private String uniqueName;
 
     public TemplateObjectTO() {
 
@@ -70,6 +72,9 @@ public class TemplateObjectTO implements DataTO {
         this.accountId = template.getAccountId();
         this.name = template.getUniqueName();
         this.format = template.getFormat();
+        this.uniqueName = template.getUniqueName();
+        this.size = template.getSize();
+
         if (template.getDataStore() != null) {
             this.imageDataStore = template.getDataStore().getTO();
         }
@@ -204,10 +209,26 @@ public class TemplateObjectTO implements DataTO {
         return physicalSize;
     }
 
+    public void setIsBootable(boolean bootable) {
+        this.bootable = bootable;
+    }
+
+    public boolean isBootable() {
+        return bootable;
+    }
+
     public void setPhysicalSize(Long physicalSize) {
         this.physicalSize = physicalSize;
     }
 
+    public String getUniqueName() {
+        return this.uniqueName;
+    }
+
+    public void setUniqueName(String uniqueName) {
+        this.uniqueName = uniqueName;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder("TemplateTO[id=").append(id).append("|origUrl=").append(origUrl).append("|name").append(name).append("]").toString();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java
index 4657316..9353499 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java
@@ -20,6 +20,8 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
 
 import java.util.List;
 
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+
 public interface EndPointSelector {
     EndPoint select(DataObject srcData, DataObject destData);
 
@@ -35,5 +37,5 @@ public interface EndPointSelector {
 
     EndPoint select(Scope scope, Long storeId);
 
-    EndPoint selectHypervisorHost(Scope scope);
+    EndPoint selectHypervisorHostByType(Scope scope, HypervisorType htype);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
index 88ce932..ff045bc 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
@@ -18,12 +18,15 @@
  */
 package org.apache.cloudstack.engine.subsystem.api.storage;
 
+import java.util.List;
+
 import org.apache.cloudstack.framework.async.AsyncCallFuture;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.command.CommandResult;
 
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.StoragePool;
+import com.cloud.utils.Ternary;
 
 public interface TemplateService {
 
@@ -65,4 +68,8 @@ public interface TemplateService {
     void associateTemplateToZone(long templateId, Long zoneId);
 
     void associateCrosszoneTemplatesToZone(long dcId);
+
+    List<Ternary<String, Long, Long>> getDatadiskTemplates(TemplateInfo template);
+
+    AsyncCallFuture<TemplateApiResult> createDatadiskTemplateAsync(TemplateInfo parentTemplate, TemplateInfo dataDiskTemplate, String path, long fileSize);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/api/src/org/apache/cloudstack/storage/image/datastore/ImageStoreEntity.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/storage/image/datastore/ImageStoreEntity.java b/engine/api/src/org/apache/cloudstack/storage/image/datastore/ImageStoreEntity.java
index 43a0f75..be419b4 100644
--- a/engine/api/src/org/apache/cloudstack/storage/image/datastore/ImageStoreEntity.java
+++ b/engine/api/src/org/apache/cloudstack/storage/image/datastore/ImageStoreEntity.java
@@ -18,16 +18,20 @@
  */
 package org.apache.cloudstack.storage.image.datastore;
 
+import java.util.List;
 import java.util.Set;
 
+import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
 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.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 
 import com.cloud.storage.ImageStore;
 import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.utils.Ternary;
 
 public interface ImageStoreEntity extends DataStore, ImageStore {
     TemplateInfo getTemplate(long templateId);
@@ -43,4 +47,8 @@ public interface ImageStoreEntity extends DataStore, ImageStore {
     String getMountPoint(); // get the mount point on ssvm.
 
     String createEntityExtractUrl(String installPath, ImageFormat format, DataObject dataObject);  // get the entity download URL
+
+    List<Ternary<String, Long, Long>> getDatadiskTemplates(DataObject obj);
+
+    Void createDataDiskTemplateAsync(TemplateInfo dataDiskTemplate, String path, long fileSize, AsyncCompletionCallback<CreateCmdResult> callback);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/schema/src/com/cloud/storage/VMTemplateVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/VMTemplateVO.java b/engine/schema/src/com/cloud/storage/VMTemplateVO.java
index 9a77cbf..ce8c549 100755
--- a/engine/schema/src/com/cloud/storage/VMTemplateVO.java
+++ b/engine/schema/src/com/cloud/storage/VMTemplateVO.java
@@ -146,6 +146,9 @@ public class VMTemplateVO implements VirtualMachineTemplate {
     @Column(name = "dynamically_scalable")
     protected boolean dynamicallyScalable;
 
+    @Column(name = "parent_template_id")
+    private Long parentTemplateId;
+
     @Override
     public String getUniqueName() {
         return uniqueName;
@@ -636,4 +639,13 @@ public class VMTemplateVO implements VirtualMachineTemplate {
     public Class<?> getEntityType() {
         return VirtualMachineTemplate.class;
     }
+
+    @Override
+    public Long getParentTemplateId() {
+        return parentTemplateId;
+    }
+
+    public void setParentTemplateId(Long parentTemplateId) {
+        this.parentTemplateId = parentTemplateId;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
index 4e6ab6b..ffdfcba 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
@@ -91,6 +91,7 @@ import com.cloud.template.TemplateManager;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.ResourceLimitService;
+import com.cloud.utils.Ternary;
 import com.cloud.utils.UriUtils;
 import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.exception.CloudRuntimeException;
@@ -134,6 +135,8 @@ public class TemplateServiceImpl implements TemplateService {
     ConfigurationDao _configDao;
     @Inject
     StorageCacheManager _cacheMgr;
+    @Inject
+    TemplateDataFactory imageFactory;
 
     class TemplateOpContext<T> extends AsyncRpcContext<T> {
         final TemplateObject template;
@@ -887,4 +890,74 @@ public class TemplateServiceImpl implements TemplateService {
             }
         }
     }
+
+    @Override
+    public List<Ternary<String, Long, Long>> getDatadiskTemplates(TemplateInfo template) {
+        List<Ternary<String, Long, Long>> dataDiskDetails = new ArrayList<Ternary<String, Long, Long>>();
+        ImageStoreEntity tmpltStore = (ImageStoreEntity)template.getDataStore();
+        dataDiskDetails = tmpltStore.getDatadiskTemplates(template);
+        return dataDiskDetails;
+    }
+
+    private class CreateDataDiskTemplateContext<T> extends AsyncRpcContext<T> {
+        private final DataObject dataDiskTemplate;
+        private final AsyncCallFuture<TemplateApiResult> future;
+
+        public CreateDataDiskTemplateContext(AsyncCompletionCallback<T> callback, DataObject dataDiskTemplate, AsyncCallFuture<TemplateApiResult> future) {
+            super(callback);
+            this.dataDiskTemplate = dataDiskTemplate;
+            this.future = future;
+        }
+
+        public AsyncCallFuture<TemplateApiResult> getFuture() {
+            return this.future;
+        }
+    }
+
+    @Override
+    public AsyncCallFuture<TemplateApiResult> createDatadiskTemplateAsync(TemplateInfo parentTemplate, TemplateInfo dataDiskTemplate, String path, long fileSize) {
+        AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<TemplateApiResult>();
+        // Make an entry for Datadisk template in template_store_ref table
+        DataStore store = parentTemplate.getDataStore();
+        TemplateObject dataDiskTemplateOnStore = (TemplateObject)store.create(dataDiskTemplate);
+        dataDiskTemplateOnStore.processEvent(ObjectInDataStoreStateMachine.Event.CreateOnlyRequested);
+        try {
+            CreateDataDiskTemplateContext<TemplateApiResult> context = new CreateDataDiskTemplateContext<TemplateApiResult>(null, dataDiskTemplateOnStore, future);
+            AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
+            caller.setCallback(caller.getTarget().createDataDiskTemplateCallback(null, null)).setContext(context);
+            ImageStoreEntity tmpltStore = (ImageStoreEntity)parentTemplate.getDataStore();
+            tmpltStore.createDataDiskTemplateAsync(dataDiskTemplate, path, fileSize, caller);
+        } catch (CloudRuntimeException ex) {
+            dataDiskTemplateOnStore.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
+            TemplateApiResult result = new TemplateApiResult(dataDiskTemplate);
+            result.setResult(ex.getMessage());
+            if (future != null) {
+                future.complete(result);
+            }
+        }
+        return future;
+    }
+
+    protected Void createDataDiskTemplateCallback(AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> callback, CreateDataDiskTemplateContext<TemplateApiResult> context) {
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Performing create datadisk template cross callback after completion");
+        }
+        DataObject dataDiskTemplate = context.dataDiskTemplate;
+        AsyncCallFuture<TemplateApiResult> future = context.getFuture();
+        CreateCmdResult result = callback.getResult();
+        TemplateApiResult dataDiskTemplateResult = new TemplateApiResult((TemplateObject)dataDiskTemplate);
+        try {
+            if (result.isSuccess()) {
+                dataDiskTemplate.processEvent(Event.OperationSuccessed, result.getAnswer());
+            } else {
+                dataDiskTemplate.processEvent(Event.OperationFailed);
+                dataDiskTemplateResult.setResult(result.getResult());
+            }
+        } catch (Exception e) {
+            s_logger.debug("Failed to process copy template cross zones callback", e);
+            dataDiskTemplateResult.setResult(e.toString());
+        }
+        future.complete(dataDiskTemplateResult);
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
index 8da7eb7..0a3b7e8 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/ImageStoreImpl.java
@@ -19,6 +19,7 @@
 package org.apache.cloudstack.storage.image.store;
 
 import java.util.Date;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
@@ -26,6 +27,7 @@ import javax.inject.Inject;
 
 import org.apache.log4j.Logger;
 
+import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
 import org.apache.cloudstack.engine.subsystem.api.storage.ImageStoreProvider;
@@ -35,6 +37,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
 import org.apache.cloudstack.framework.async.AsyncCallFuture;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.command.CommandResult;
 import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
@@ -47,6 +50,7 @@ import com.cloud.capacity.dao.CapacityDao;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.utils.Ternary;
 import com.cloud.utils.component.ComponentContext;
 
 public class ImageStoreImpl implements ImageStoreEntity {
@@ -203,4 +207,14 @@ public class ImageStoreImpl implements ImageStoreEntity {
         return driver.createEntityExtractUrl(this, installPath, format, dataObject);
     }
 
+    @Override
+    public List<Ternary<String, Long, Long>> getDatadiskTemplates(DataObject obj) {
+        return driver.getDatadiskTemplates(obj);
+    }
+
+    @Override
+    public Void createDataDiskTemplateAsync(TemplateInfo dataDiskTemplate, String path, long fileSize, AsyncCompletionCallback<CreateCmdResult> callback) {
+        return driver.createDataDiskTemplateAsync(dataDiskTemplate, path, fileSize, callback);
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
index 7288d45..5649028 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@ -38,6 +38,7 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.cloudstack.storage.to.TemplateObjectTO;
 
 import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateAnswer;
 import com.cloud.agent.api.to.DataObjectType;
 import com.cloud.agent.api.to.DataTO;
 import com.cloud.exception.ConcurrentOperationException;
@@ -220,6 +221,16 @@ public class TemplateObject implements TemplateInfo {
                         templateVO.setSize(newTemplate.getSize());
                         imageDao.update(templateVO.getId(), templateVO);
                     }
+                } else if (answer instanceof CreateDatadiskTemplateAnswer) {
+                    CreateDatadiskTemplateAnswer createAnswer = (CreateDatadiskTemplateAnswer)answer;
+                    TemplateObjectTO dataDiskTemplate = createAnswer.getDataDiskTemplate();
+                    TemplateDataStoreVO templateStoreRef = templateStoreDao.findByStoreTemplate(getDataStore().getId(), dataDiskTemplate.getId());
+                    templateStoreRef.setInstallPath(dataDiskTemplate.getPath());
+                    templateStoreRef.setDownloadPercent(100);
+                    templateStoreRef.setDownloadState(Status.DOWNLOADED);
+                    templateStoreRef.setSize(dataDiskTemplate.getSize());
+                    templateStoreRef.setPhysicalSize(dataDiskTemplate.getPhysicalSize());
+                    templateStoreDao.update(templateStoreRef.getId(), templateStoreRef);
                 }
             }
             objectInStoreMgr.update(this, event);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
----------------------------------------------------------------------
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 304f959..73c0efb 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
@@ -47,6 +47,7 @@ import com.cloud.host.HostVO;
 import com.cloud.host.Status;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ScopeType;
 import com.cloud.storage.Storage.TemplateType;
@@ -66,6 +67,7 @@ public class DefaultEndPointSelector implements EndPointSelector {
         "select h.id from host h, storage_pool_host_ref s  where h.status = 'Up' and h.type = 'Routing' and h.resource_state = 'Enabled' and"
             + " h.id = s.host_id and s.pool_id = ? ";
     private String findOneHypervisorHostInScope = "select h.id from host h where h.status = 'Up' and h.hypervisor_type is not null ";
+    private String findOneHypervisorHostInScopeByType = "select h.id from host h where h.status = 'Up' and h.hypervisor_type = ? ";
 
     protected boolean moveBetweenPrimaryImage(DataStore srcStore, DataStore destStore) {
         DataStoreRole srcRole = srcStore.getRole();
@@ -346,9 +348,13 @@ public class DefaultEndPointSelector implements EndPointSelector {
     }
 
     @Override
-    public EndPoint selectHypervisorHost(Scope scope) {
+    public EndPoint selectHypervisorHostByType(Scope scope, HypervisorType htype) {
         StringBuilder sbuilder = new StringBuilder();
-        sbuilder.append(findOneHypervisorHostInScope);
+        if (htype != null) {
+            sbuilder.append(findOneHypervisorHostInScopeByType);
+        } else {
+            sbuilder.append(findOneHypervisorHostInScope);
+        }
         if (scope.getScopeType() == ScopeType.ZONE) {
             sbuilder.append(" and h.data_center_id = ");
             sbuilder.append(scope.getScopeId());
@@ -366,6 +372,9 @@ public class DefaultEndPointSelector implements EndPointSelector {
 
         try {
             pstmt = txn.prepareStatement(sql);
+            if (htype != null) {
+                pstmt.setString(1, htype.toString());
+            }
             rs = pstmt.executeQuery();
             while (rs.next()) {
                 long id = rs.getLong(1);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
index 7ed11ec..94366ef 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
@@ -20,7 +20,9 @@ package org.apache.cloudstack.storage.image;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
 
 import javax.inject.Inject;
@@ -33,6 +35,7 @@ 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.EndPoint;
 import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.framework.async.AsyncRpcContext;
@@ -43,18 +46,27 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
+import org.apache.cloudstack.storage.endpoint.DefaultEndPointSelector;
 
 import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateCommand;
 import com.cloud.agent.api.storage.DownloadAnswer;
+import com.cloud.agent.api.storage.GetDatadisksAnswer;
+import com.cloud.agent.api.storage.GetDatadisksCommand;
 import com.cloud.agent.api.storage.Proxy;
 import com.cloud.agent.api.to.DataObjectType;
 import com.cloud.agent.api.to.DataTO;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.VMTemplateStorageResourceAssoc;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VMTemplateDetailsDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.download.DownloadMonitor;
+import com.cloud.user.ResourceLimitService;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.Ternary;
 
 public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
     private static final Logger s_logger = Logger.getLogger(BaseImageStoreDriverImpl.class);
@@ -69,9 +81,17 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
     @Inject
     TemplateDataStoreDao _templateStoreDao;
     @Inject
+    VMTemplateDetailsDao _templateDetailsDao;
+    @Inject
     EndPointSelector _epSelector;
     @Inject
-    ConfigurationDao configDao;
+    ConfigurationDao configDao;;
+    @Inject
+    DefaultEndPointSelector _defaultEpSelector;
+    @Inject
+    AccountDao _accountDao;
+    @Inject
+    ResourceLimitService _resourceLimitMgr;
     protected String _proxy = null;
 
     protected Proxy getHttpProxy() {
@@ -143,6 +163,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
         DataObject obj = context.data;
         DataStore store = obj.getDataStore();
 
+        VMTemplateVO vmTemplate = _templateDao.findById(obj.getId());
         TemplateDataStoreVO tmpltStoreVO = _templateStoreDao.findByStoreTemplate(store.getId(), obj.getId());
         if (tmpltStoreVO != null) {
             if (tmpltStoreVO.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
@@ -182,7 +203,6 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
                 templateDaoBuilder.setChecksum(answer.getCheckSum());
                 _templateDao.update(obj.getId(), templateDaoBuilder);
             }
-
             CreateCmdResult result = new CreateCmdResult(null, null);
             caller.complete(result);
         }
@@ -271,4 +291,54 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
     @Override
     public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
     }
+
+    @Override
+    public List<Ternary<String, Long, Long>> getDatadiskTemplates(DataObject obj) {
+        List<Ternary<String, Long, Long>> dataDiskDetails = new ArrayList<Ternary<String, Long, Long>>();
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Get the data disks present in the OVA template");
+        }
+        DataStore store = obj.getDataStore();
+        GetDatadisksCommand cmd = new GetDatadisksCommand(obj.getTO());
+        EndPoint ep = _defaultEpSelector.selectHypervisorHostByType(store.getScope(), HypervisorType.VMware);
+        Answer answer = null;
+        if (ep == null) {
+            String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+            s_logger.error(errMsg);
+            answer = new Answer(cmd, false, errMsg);
+        } else {
+            answer = ep.sendMessage(cmd);
+        }
+        if (answer != null && answer.getResult()) {
+            GetDatadisksAnswer getDatadisksAnswer = (GetDatadisksAnswer)answer;
+            dataDiskDetails = getDatadisksAnswer.getDataDiskDetails(); // Details - Disk path, virtual size
+        }
+        return dataDiskDetails;
+    }
+
+    @Override
+    public Void createDataDiskTemplateAsync(TemplateInfo dataDiskTemplate, String path, long fileSize,
+            AsyncCompletionCallback<CreateCmdResult> callback) {
+        Answer answer = null;
+        String errMsg = null;
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Create Datadisk template: " + dataDiskTemplate.getId());
+        }
+        CreateDatadiskTemplateCommand cmd = new CreateDatadiskTemplateCommand(dataDiskTemplate.getTO(), path, fileSize);
+        EndPoint ep = _defaultEpSelector.selectHypervisorHostByType(dataDiskTemplate.getDataStore().getScope(), HypervisorType.VMware);
+        if (ep == null) {
+            errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+            s_logger.error(errMsg);
+            answer = new Answer(cmd, false, errMsg);
+        } else {
+            answer = ep.sendMessage(cmd);
+        }
+        if (answer != null && !answer.getResult()) {
+            errMsg = answer.getDetails();
+        }
+        CreateCmdResult result = new CreateCmdResult(null, answer);
+        result.setResult(errMsg);
+        callback.complete(result);
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/engine/storage/src/org/apache/cloudstack/storage/image/ImageStoreDriver.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/ImageStoreDriver.java b/engine/storage/src/org/apache/cloudstack/storage/image/ImageStoreDriver.java
index fa7ea37..e4e9451 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/ImageStoreDriver.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/ImageStoreDriver.java
@@ -18,12 +18,22 @@
  */
 package org.apache.cloudstack.storage.image;
 
+import java.util.List;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
 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.DataStoreDriver;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 
 import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.utils.Ternary;
 
 public interface ImageStoreDriver extends DataStoreDriver {
     String createEntityExtractUrl(DataStore store, String installPath, ImageFormat format, DataObject dataObject);
+
+    List<Ternary<String, Long, Long>> getDatadiskTemplates(DataObject obj);
+
+    Void createDataDiskTemplateAsync(TemplateInfo dataDiskTemplate, String path, long fileSize, AsyncCompletionCallback<CreateCmdResult> callback);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
index f78f370..9e16e35 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
@@ -27,7 +27,9 @@ import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.DeleteVMSnapshotCommand;
 import com.cloud.agent.api.RevertToVMSnapshotCommand;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateCommand;
 import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.GetDatadisksCommand;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
 
 public interface VmwareStorageManager {
@@ -49,6 +51,10 @@ public interface VmwareStorageManager {
 
     Answer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd);
 
+    Answer execute(VmwareHostService hostService, CreateDatadiskTemplateCommand cmd);
+
+    Answer execute(VmwareHostService hostService, GetDatadisksCommand cmd);
+
     boolean execute(VmwareHostService hostService, CreateEntityDownloadURLCommand cmd);
 
     public void createOva(String path, String name);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
index c1b907e..9a3ce40 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
@@ -57,8 +57,12 @@ import com.cloud.agent.api.RevertToVMSnapshotAnswer;
 import com.cloud.agent.api.RevertToVMSnapshotCommand;
 import com.cloud.agent.api.storage.CopyVolumeAnswer;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateAnswer;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateCommand;
 import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
 import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
+import com.cloud.agent.api.storage.GetDatadisksAnswer;
+import com.cloud.agent.api.storage.GetDatadisksCommand;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
 import com.cloud.agent.api.to.DataObjectType;
@@ -80,6 +84,7 @@ import com.cloud.storage.JavaStorageLayer;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.StorageLayer;
 import com.cloud.storage.Volume;
+import com.cloud.storage.resource.VmwareStorageLayoutHelper;
 import com.cloud.storage.template.OVAProcessor;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.Pair;
@@ -543,6 +548,160 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
         return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName);
     }
 
+    @Override
+    public Answer execute(VmwareHostService hostService, GetDatadisksCommand cmd) {
+        List<Ternary<String, Long, Long>> datDiskDetails = new ArrayList<Ternary<String, Long, Long>>();
+        DataTO srcData = cmd.getData();
+        TemplateObjectTO template = (TemplateObjectTO)srcData;
+        DataStoreTO srcStore = srcData.getDataStore();
+        if (!(srcStore instanceof NfsTO)) {
+            return new CreateDatadiskTemplateAnswer("Unsupported protocol");
+        }
+        NfsTO nfsImageStore = (NfsTO)srcStore;
+        String secondaryStorageUrl = nfsImageStore.getUrl();
+        assert (secondaryStorageUrl != null);
+        String secondaryStorageUuid = HypervisorHostHelper.getSecondaryDatastoreUUID(secondaryStorageUrl).replace("-", "");
+        String templateUrl = secondaryStorageUrl + "/" + srcData.getPath();
+        Pair<String, String> templateInfo = VmwareStorageLayoutHelper.decodeTemplateRelativePathAndNameFromUrl(secondaryStorageUrl, templateUrl, template.getName());
+        String templateRelativeFolderPath = templateInfo.first();
+
+        VmwareContext context = hostService.getServiceContext(cmd);
+        try {
+            VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
+
+            ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, secondaryStorageUuid);
+            DatastoreMO datastoreMo = new DatastoreMO(context, morDs);
+
+            String secondaryMountPoint = _mountService.getMountPoint(secondaryStorageUrl);
+            s_logger.info("Secondary storage mount point: " + secondaryMountPoint);
+
+            String srcOVAFileName = VmwareStorageLayoutHelper.getTemplateOnSecStorageFilePath(secondaryMountPoint, templateRelativeFolderPath, templateInfo.second(),
+                    ImageFormat.OVA.getFileExtension());
+
+            String ovfFilePath = getOVFFilePath(srcOVAFileName);
+            if (ovfFilePath == null) {
+                Script command = new Script("tar", 0, s_logger);
+                command.add("--no-same-owner");
+                command.add("-xf", srcOVAFileName);
+                command.setWorkDir(secondaryMountPoint + "/" + templateRelativeFolderPath);
+                s_logger.info("Executing command: " + command.toString());
+                String result = command.execute();
+                if (result != null) {
+                    String msg = "Unable to unpack snapshot OVA file at: " + srcOVAFileName;
+                    s_logger.error(msg);
+                    throw new Exception(msg);
+                }
+            }
+
+            ovfFilePath = getOVFFilePath(srcOVAFileName);
+            if (ovfFilePath == null) {
+                String msg = "Unable to locate OVF file in template package directory: " + srcOVAFileName;
+                s_logger.error(msg);
+                throw new Exception(msg);
+            }
+
+            s_logger.debug("Reading OVF " + ovfFilePath + " to retrive the number of disks present in OVA");
+            List<Pair<String, Boolean>> ovfVolumeDetails = HypervisorHostHelper.readOVF(hyperHost, ovfFilePath, datastoreMo);
+
+            // Get the virtual size of data disk
+            for (Pair<String, Boolean> ovfVolumeDetail : ovfVolumeDetails) {
+                if (ovfVolumeDetail.second()) { // ROOT disk
+                    continue;
+                }
+                String dataDiskPath = ovfVolumeDetail.first();
+                String diskName = dataDiskPath.substring((dataDiskPath.lastIndexOf(File.separator)) + 1);
+                Pair<Long, Long> diskDetails = new OVAProcessor().getDiskDetails(ovfFilePath, diskName);
+                datDiskDetails.add(new Ternary<String, Long, Long>(dataDiskPath, diskDetails.first(), diskDetails.second()));
+            }
+        } catch (Exception e) {
+            String msg = "Get Datadisk Template Count failed due to " + e.getMessage();
+            s_logger.error(msg);
+            return new GetDatadisksAnswer(msg);
+        }
+        return new GetDatadisksAnswer(datDiskDetails);
+    }
+
+    @Override
+    public Answer execute(VmwareHostService hostService, CreateDatadiskTemplateCommand cmd) {
+        TemplateObjectTO diskTemplate = new TemplateObjectTO();
+        TemplateObjectTO dataDiskTemplate = (TemplateObjectTO)cmd.getDataDiskTemplate();
+        DataStoreTO dataStore = dataDiskTemplate.getDataStore();
+        if (!(dataStore instanceof NfsTO)) {
+            return new CreateDatadiskTemplateAnswer("Unsupported protocol");
+        }
+        NfsTO nfsImageStore = (NfsTO)dataStore;
+        String secondaryStorageUrl = nfsImageStore.getUrl();
+        assert (secondaryStorageUrl != null);
+        String secondaryStorageUuid = HypervisorHostHelper.getSecondaryDatastoreUUID(secondaryStorageUrl).replace("-", "");
+
+        VmwareContext context = hostService.getServiceContext(cmd);
+        try {
+            VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
+
+            ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, secondaryStorageUuid);
+            DatastoreMO datastoreMo = new DatastoreMO(context, morDs);
+
+            String secondaryMountPoint = _mountService.getMountPoint(secondaryStorageUrl);
+            s_logger.info("Secondary storage mount point: " + secondaryMountPoint);
+
+            long templateId = dataDiskTemplate.getId();
+            String templateUniqueName = dataDiskTemplate.getUniqueName();
+            String dataDiskPath = cmd.getPath();
+            long virtualSize = dataDiskTemplate.getSize();
+            long fileSize = cmd.getFileSize();
+            String diskName = dataDiskPath.substring((dataDiskPath.lastIndexOf(File.separator)) + 1);
+            long physicalSize = new File(dataDiskPath).length();
+            String dataDiskTemplateFolderPath = getTemplateRelativeDirInSecStorage(dataDiskTemplate.getAccountId(), dataDiskTemplate.getId());
+            String dataDiskTemplateFolderFullPath = secondaryMountPoint + "/" + dataDiskTemplateFolderPath;
+
+            // Create folder to hold datadisk template
+            synchronized (dataDiskTemplateFolderPath.intern()) {
+                Script command = new Script(false, "mkdir", _timeout, s_logger);
+                command.add("-p");
+                command.add(dataDiskTemplateFolderFullPath);
+                String result = command.execute();
+                if (result != null) {
+                    String msg = "Unable to prepare template directory: " + dataDiskTemplateFolderPath + ", storage: " + secondaryStorageUrl + ", error msg: " + result;
+                    s_logger.error(msg);
+                    throw new Exception(msg);
+                }
+            }
+
+            // Copy Datadisk VMDK from parent template folder to Datadisk template folder
+            synchronized (dataDiskPath.intern()) {
+                Script command = new Script(false, "cp", _timeout, s_logger);
+                command.add(dataDiskPath);
+                command.add(dataDiskTemplateFolderFullPath);
+                String result = command.execute();
+                if (result != null) {
+                    String msg = "Unable to copy VMDK from parent template folder to datadisk template folder" + ", error msg: " + result;
+                    s_logger.error(msg);
+                    throw new Exception(msg);
+                }
+            }
+
+            String ovfName = diskName.substring(0, diskName.lastIndexOf("-"));
+            String datastorePath = String.format("[%s] %s", datastoreMo.getName(), dataDiskTemplateFolderPath);
+
+            // Create OVF for Datadisk
+            s_logger.debug("Creating OVF file for datadisk " + diskName + " in " + dataDiskTemplateFolderFullPath);
+            HypervisorHostHelper.createOvfFile(hyperHost, diskName, ovfName, datastorePath, dataDiskTemplateFolderFullPath, virtualSize, fileSize, morDs);
+
+            postCreatePrivateTemplate(dataDiskTemplateFolderFullPath, templateId, templateUniqueName, physicalSize, virtualSize);
+            writeMetaOvaForTemplate(dataDiskTemplateFolderFullPath, ovfName + ".ovf", diskName, templateUniqueName, physicalSize);
+
+            diskTemplate.setId(templateId);
+            diskTemplate.setPath(dataDiskTemplateFolderPath + "/" + templateUniqueName + ".ova");
+            diskTemplate.setSize(virtualSize);
+            diskTemplate.setPhysicalSize(physicalSize);
+        } catch (Exception e) {
+            String msg = "Create Datadisk template failed due to " + e.getMessage();
+            s_logger.error(msg);
+            return new CreateDatadiskTemplateAnswer(msg);
+        }
+        return new CreateDatadiskTemplateAnswer(diskTemplate);
+    }
+
     // templateName: name in secondary storage
     // templateUuid: will be used at hypervisor layer
     private void copyTemplateFromSecondaryToPrimary(VmwareHypervisorHost hyperHost, DatastoreMO datastoreMo, String secondaryStorageUrl,

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 0024b44..2991e85 100755
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -188,8 +188,12 @@ import com.cloud.agent.api.routing.SetNetworkACLCommand;
 import com.cloud.agent.api.routing.SetSourceNatCommand;
 import com.cloud.agent.api.storage.CopyVolumeAnswer;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateAnswer;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateCommand;
 import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
 import com.cloud.agent.api.storage.DestroyCommand;
+import com.cloud.agent.api.storage.GetDatadisksAnswer;
+import com.cloud.agent.api.storage.GetDatadisksCommand;
 import com.cloud.agent.api.storage.MigrateVolumeAnswer;
 import com.cloud.agent.api.storage.MigrateVolumeCommand;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
@@ -430,6 +434,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 answer = execute((GetStorageStatsCommand)cmd);
             } else if (clz == PrimaryStorageDownloadCommand.class) {
                 answer = execute((PrimaryStorageDownloadCommand)cmd);
+            } else if (clz == CreateDatadiskTemplateCommand.class) {
+                answer = execute((CreateDatadiskTemplateCommand)cmd);
+            } else if (clz == GetDatadisksCommand.class) {
+                answer = execute((GetDatadisksCommand)cmd);
             } else if (clz == GetVncPortCommand.class) {
                 answer = execute((GetVncPortCommand)cmd);
             } else if (clz == SetupCommand.class) {
@@ -3724,6 +3732,44 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         }
     }
 
+    protected GetDatadisksAnswer execute(GetDatadisksCommand cmd) {
+        if (s_logger.isInfoEnabled()) {
+            s_logger.info("Executing resource GetDatadisksCommand: " + _gson.toJson(cmd));
+        }
+        try {
+            VmwareContext context = getServiceContext();
+            VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
+            return (GetDatadisksAnswer)mgr.getStorageManager().execute(this, cmd);
+        } catch (Throwable e) {
+            if (e instanceof RemoteException) {
+                s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context");
+                invalidateServiceContext();
+            }
+            String msg = "GetDatadisksCommand failed due to " + VmwareHelper.getExceptionMessage(e);
+            s_logger.error(msg, e);
+            return new GetDatadisksAnswer(msg);
+        }
+    }
+
+    protected CreateDatadiskTemplateAnswer execute(CreateDatadiskTemplateCommand cmd) {
+        if (s_logger.isInfoEnabled()) {
+            s_logger.info("Executing resource CreateDatadiskTemplatesCommand: " + _gson.toJson(cmd));
+        }
+        try {
+            VmwareContext context = getServiceContext();
+            VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
+            return (CreateDatadiskTemplateAnswer)mgr.getStorageManager().execute(this, cmd);
+        } catch (Throwable e) {
+            if (e instanceof RemoteException) {
+                s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context");
+                invalidateServiceContext();
+            }
+            String msg = "CreateDatadiskTemplatesCommand failed due to " + VmwareHelper.getExceptionMessage(e);
+            s_logger.error(msg, e);
+            return new CreateDatadiskTemplateAnswer(msg);
+        }
+    }
+
     protected Answer execute(PvlanSetupCommand cmd) {
         // Pvlan related operations are performed in the start/stop command paths
         // for vmware. This function is implemented to support mgmt layer code

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java
index f633774..a7f647b 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java
@@ -33,7 +33,9 @@ import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
+import com.cloud.agent.api.storage.CreateDatadiskTemplateCommand;
 import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.GetDatadisksCommand;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
 import com.cloud.hypervisor.vmware.manager.VmwareHostService;
 import com.cloud.hypervisor.vmware.manager.VmwareStorageManager;
@@ -98,6 +100,10 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe
                 answer = storageSubsystemHandler.handleStorageCommands((StorageSubSystemCommand)cmd);
             } else if (cmd instanceof CreateEntityDownloadURLCommand) {
                 answer = execute((CreateEntityDownloadURLCommand)cmd);
+            } else if (cmd instanceof CreateDatadiskTemplateCommand) {
+                answer = execute((CreateDatadiskTemplateCommand)cmd);
+            } else if (cmd instanceof GetDatadisksCommand) {
+                answer = execute((GetDatadisksCommand)cmd);
             } else {
                 answer = _resource.defaultAction(cmd);
             }
@@ -174,6 +180,14 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe
         return _storageMgr.execute(this, cmd);
     }
 
+    private Answer execute(CreateDatadiskTemplateCommand cmd) {
+        return _storageMgr.execute(this, cmd);
+    }
+
+    private Answer execute(GetDatadisksCommand cmd) {
+        return _storageMgr.execute(this, cmd);
+    }
+
     @Override
     public VmwareContext getServiceContext(Command cmd) {
         String guid = cmd.getContextParam("guid");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java
index 89e4ab5..40fc1d8 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java
@@ -145,7 +145,7 @@ public class XenServerGuru extends HypervisorGuruBase implements HypervisorGuru
                 DataStoreTO destStore = destData.getDataStore();
                 if (srcStore instanceof NfsTO && destStore instanceof NfsTO) {
                     HostVO host = hostDao.findById(hostId);
-                    EndPoint ep = endPointSelector.selectHypervisorHost(new ZoneScope(host.getDataCenterId()));
+                    EndPoint ep = endPointSelector.selectHypervisorHostByType(new ZoneScope(host.getDataCenterId()), null);
                     host = hostDao.findById(ep.getId());
                     hostDao.loadDetails(host);
                     String snapshotHotFixVersion = host.getDetail(XenserverConfigs.XS620HotFix);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/server/src/com/cloud/template/HypervisorTemplateAdapter.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/com/cloud/template/HypervisorTemplateAdapter.java
index 51dedf7..46f0bd1 100755
--- a/server/src/com/cloud/template/HypervisorTemplateAdapter.java
+++ b/server/src/com/cloud/template/HypervisorTemplateAdapter.java
@@ -19,6 +19,7 @@ package com.cloud.template;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -48,6 +49,7 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.framework.async.AsyncRpcContext;
 import org.apache.cloudstack.framework.messagebus.MessageBus;
 import org.apache.cloudstack.framework.messagebus.PublishScope;
+import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
 
@@ -72,9 +74,13 @@ import com.cloud.storage.dao.VMTemplateZoneDao;
 import com.cloud.storage.download.DownloadMonitor;
 import com.cloud.user.Account;
 import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
 import com.cloud.utils.UriUtils;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.db.EntityManager;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionStatus;
 import com.cloud.utils.exception.CloudRuntimeException;
 
 @Local(value = TemplateAdapter.class)
@@ -96,6 +102,8 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
     @Inject
     AlertManager alertMgr;
     @Inject
+    TemplateDataStoreDao _tmplStoreDao;
+    @Inject
     VMTemplateZoneDao templateZoneDao;
     @Inject
     EndPointSelector _epSelector;
@@ -290,6 +298,10 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
         TemplateInfo template = context.template;
         if (result.isSuccess()) {
             VMTemplateVO tmplt = _tmpltDao.findById(template.getId());
+            // Check if OVA contains additional data disks. If yes, create Datadisk templates for each of the additional datadisk present in the OVA
+            if (template.getFormat().equals(ImageFormat.OVA)) {
+                createDataDiskTemplates(template);
+            }
             // need to grant permission for public templates
             if (tmplt.isPublicTemplate()) {
                 _messageBus.publish(_name, TemplateManager.MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT, PublishScope.LOCAL, tmplt.getId());
@@ -331,6 +343,60 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
         return null;
     }
 
+    private void createDataDiskTemplates(TemplateInfo parentTemplate) {
+        TemplateApiResult result = null;
+        VMTemplateVO template = _tmpltDao.findById(parentTemplate.getId());
+        DataStore imageStore = parentTemplate.getDataStore();
+        List<Ternary<String, Long, Long>> dataDiskTemplates = imageService.getDatadiskTemplates(parentTemplate);
+        s_logger.error("Found " + dataDiskTemplates.size() + " Datadisk templates for template: " + parentTemplate.getId());
+        int diskCount = 1;
+        for (Ternary<String, Long, Long> dataDiskTemplate : dataDiskTemplates) {
+            // Make an entry in vm_template table
+            final long templateId = _templateDao.getNextInSequence(Long.class, "id");
+            VMTemplateVO templateVO = new VMTemplateVO(templateId, template.getName() + "-DataDiskTemplate-" + diskCount, template.getFormat(), false, false, false,
+                    TemplateType.DATADISK, template.getUrl(), template.requiresHvm(), template.getBits(), template.getAccountId(), null,
+                    template.getDisplayText() + "-DataDiskTemplate", false, 0, false, template.getHypervisorType(), null, null, false, false);
+            templateVO.setParentTemplateId(template.getId());
+            templateVO.setSize(dataDiskTemplate.second());
+            templateVO = _templateDao.persist(templateVO);
+            // Make sync call to create Datadisk templates in image store
+            TemplateInfo dataDiskTemplateInfo = imageFactory.getTemplate(templateVO.getId(), imageStore);
+            AsyncCallFuture<TemplateApiResult> future = imageService.createDatadiskTemplateAsync(parentTemplate, dataDiskTemplateInfo, dataDiskTemplate.first(),
+                    dataDiskTemplate.third());
+            try {
+                result = future.get();
+                if (result.isSuccess()) {
+                    // Make an entry in template_zone_ref table
+                    if (imageStore.getScope().getScopeType() == ScopeType.REGION) {
+                        imageService.associateTemplateToZone(templateId, null);
+                    } else if (imageStore.getScope().getScopeType() == ScopeType.ZONE) {
+                        Long zoneId = ((ImageStoreEntity)imageStore).getDataCenterId();
+                        VMTemplateZoneVO templateZone = new VMTemplateZoneVO(zoneId, templateId, new Date());
+                        _tmpltZoneDao.persist(templateZone);
+                    }
+                    _resourceLimitMgr.incrementResourceCount(template.getAccountId(), ResourceType.secondary_storage, templateVO.getSize());
+                } else {
+                    // Cleanup Datadisk template enries in case of failure
+                    Transaction.execute(new TransactionCallbackNoReturn() {
+                        @Override
+                        public void doInTransactionWithoutResult(TransactionStatus status) {
+                            _tmplStoreDao.deletePrimaryRecordsForTemplate(templateId);
+                            _tmpltZoneDao.deletePrimaryRecordsForTemplate(templateId);
+                            _tmpltDao.expunge(templateId);
+                        }
+                    });
+                    // Continue to create the remaining Datadisk templates even if creation of 1 Datadisk template failes
+                    s_logger.error("Creation of Datadisk: " + templateVO.getId() + " failed: " + result.getResult());
+                    continue;
+                }
+            } catch (Exception e) {
+                s_logger.error("Creation of Datadisk: " + templateVO.getId() + " failed: " + result.getResult());
+                continue;
+            }
+            diskCount++;
+        }
+    }
+
     @Override
     @DB
     public boolean delete(TemplateProfile profile) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/server/src/com/cloud/template/TemplateManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index ead841f..1268f24 100755
--- a/server/src/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/com/cloud/template/TemplateManagerImpl.java
@@ -680,6 +680,11 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
             throw new InvalidParameterValueException("Unable to find template with id");
         }
 
+        // Verify template is not Datadisk template
+        if (template.getTemplateType().equals(TemplateType.DATADISK)) {
+            throw new InvalidParameterValueException("Template " + template.getId() + " is of type Datadisk. Cannot copy Datadisk templates.");
+        }
+
         DataStore srcSecStore = null;
         if (sourceZoneId != null) {
             // template is on zone-wide secondary storage

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/setup/db/db/schema-440to450.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-440to450.sql b/setup/db/db/schema-440to450.sql
index 4cc4879..1fbb312 100644
--- a/setup/db/db/schema-440to450.sql
+++ b/setup/db/db/schema-440to450.sql
@@ -223,3 +223,5 @@ CREATE VIEW `cloud`.`volume_view` AS
         `cloud`.`async_job` ON async_job.instance_id = volumes.id
             and async_job.instance_type = 'Volume'
             and async_job.job_status = 0;
+
+ALTER TABLE `cloud`.`vm_template` ADD COLUMN `parent_template_id` bigint(20) unsigned DEFAULT NULL COMMENT 'If datadisk template, then id of the root template this template belongs to';

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/902f231e/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index 2a8474b..5b17019 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -85,7 +85,6 @@ import com.cloud.hypervisor.vmware.util.VmwareHelper;
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.utils.ActionDelegate;
 import com.cloud.utils.Pair;
-import com.cloud.utils.Ternary;
 import com.cloud.utils.cisco.n1kv.vsm.NetconfHelper;
 import com.cloud.utils.cisco.n1kv.vsm.PolicyMap;
 import com.cloud.utils.cisco.n1kv.vsm.PortProfile;
@@ -104,7 +103,6 @@ public class HypervisorHostHelper {
 
     // make vmware-base loosely coupled with cloud-specific stuff, duplicate VLAN.UNTAGGED constant here
     private static final String UNTAGGED_VLAN_NAME = "untagged";
-    private static final String VMDK_PACK_DIR = "ova";
     private static final String OVA_OPTION_KEY_BOOTDISK = "cloud.ova.bootdisk";
 
     public static VirtualMachineMO findVmFromObjectContent(VmwareContext context, ObjectContent[] ocs, String name, String instanceNameCustomField) {
@@ -142,6 +140,10 @@ public class HypervisorHostHelper {
         return morDs;
     }
 
+    public static String getSecondaryDatastoreUUID(String storeUrl) {
+        return UUID.nameUUIDFromBytes(storeUrl.getBytes()).toString();
+    }
+
     public static DatastoreMO getHyperHostDatastoreMO(VmwareHypervisorHost hyperHost, String datastoreName) throws Exception {
         ObjectContent[] ocs = hyperHost.getDatastorePropertiesOnHyperHost(new String[] {"name"});
         if (ocs != null && ocs.length > 0) {
@@ -1188,10 +1190,13 @@ public class HypervisorHostHelper {
         return false;
     }
 
-    public static List<Ternary<String, Long, Boolean>> readOVF(VmwareHypervisorHost host, String ovfFilePath, DatastoreMO dsMo, ManagedObjectReference morRp,
-            ManagedObjectReference morHost) throws Exception {
+    public static List<Pair<String, Boolean>> readOVF(VmwareHypervisorHost host, String ovfFilePath, DatastoreMO dsMo) throws Exception {
+        List<Pair<String, Boolean>> ovfVolumeInfos = new ArrayList<Pair<String, Boolean>>();
+        List<String> files = new ArrayList<String>();
 
+        ManagedObjectReference morRp = host.getHyperHostOwnerResourcePool();
         assert (morRp != null);
+        ManagedObjectReference morHost = host.getMor();
 
         String importEntityName = UUID.randomUUID().toString();
         OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams();
@@ -1211,7 +1216,7 @@ public class HypervisorHostHelper {
             throw new Exception(msg);
         }
 
-        if(!ovfImportResult.getError().isEmpty()) {
+        if (!ovfImportResult.getError().isEmpty()) {
             for (LocalizedMethodFault fault : ovfImportResult.getError()) {
                 s_logger.error("createImportSpec error: " + fault.getLocalizedMessage());
             }
@@ -1224,8 +1229,7 @@ public class HypervisorHostHelper {
             }
         }
 
-        VirtualMachineImportSpec importSpec = new VirtualMachineImportSpec();
-        importSpec = (VirtualMachineImportSpec)ovfImportResult.getImportSpec();
+        VirtualMachineImportSpec importSpec = (VirtualMachineImportSpec)ovfImportResult.getImportSpec();
         if (importSpec == null) {
             String msg = "createImportSpec() failed to create import specification for OVF template at " + ovfFilePath;
             s_logger.error(msg);
@@ -1233,15 +1237,8 @@ public class HypervisorHostHelper {
         }
 
         File ovfFile = new File(ovfFilePath);
-        int diskCount = 0;
-        long sizeKb = 0;
-        List<Ternary<String, Long, Boolean>> ovfVolumeInfos = new ArrayList<Ternary<String, Long, Boolean>>();
-        Ternary<String, Long, Boolean> ovfVolumeInfo = null;
-        List<String> files = new ArrayList<String>();
-        String absFile = null;
-
         for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem()) {
-            absFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath();
+            String absFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath();
             files.add(absFile);
         }
 
@@ -1256,19 +1253,21 @@ public class HypervisorHostHelper {
             }
         }
 
-        Boolean osDisk = true;
+        int diskCount = 0;
+        int deviceCount = 0;
         List<VirtualDeviceConfigSpec> deviceConfigList = config.getDeviceChange();
         for (VirtualDeviceConfigSpec deviceSpec : deviceConfigList) {
+            Boolean osDisk = false;
             VirtualDevice device = deviceSpec.getDevice();
             if (device instanceof VirtualDisk) {
-                sizeKb = ((VirtualDisk)device).getCapacityInKB();
-                if (diskCount == osDiskSeqNumber) {
+                if ((osDiskSeqNumber == 0 && diskCount == 0) || osDiskSeqNumber == deviceCount) {
                     osDisk = true;
                 }
-                diskCount++;
-                ovfVolumeInfo = new Ternary<String, Long, Boolean>(files.get(diskCount), sizeKb, osDisk);
+                Pair<String, Boolean> ovfVolumeInfo = new Pair<String, Boolean>(files.get(diskCount), osDisk);
                 ovfVolumeInfos.add(ovfVolumeInfo);
+                diskCount++;
             }
+            deviceCount++;
         }
         return ovfVolumeInfos;
     }
@@ -1338,46 +1337,48 @@ public class HypervisorHostHelper {
         return paramVal;
     }
 
-    public static void createOvfFile(VmwareHypervisorHost host, String diskFileName, String ovfName, String dir, long size, ManagedObjectReference morDs) throws Exception {
+    public static void createOvfFile(VmwareHypervisorHost host, String diskFileName, String ovfName, String datastorePath, String templatePath, long diskCapacity, long fileSize,
+            ManagedObjectReference morDs) throws Exception {
         VmwareContext context = host.getContext();
+        ManagedObjectReference morOvf = context.getServiceContent().getOvfManager();
         VirtualMachineMO workerVmMo = HypervisorHostHelper.createWorkerVM(host, new DatastoreMO(context, morDs), ovfName);
         if (workerVmMo == null)
             throw new Exception("Unable to find just-created worker VM");
 
-        String[] disks = {dir + File.separator + diskFileName};
-        boolean bSuccess = false;
+        String[] disks = {datastorePath + File.separator + diskFileName};
         try {
             VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
             VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
 
-            VirtualDisk device = VmwareHelper.prepareDiskDevice(workerVmMo, null, -1, disks, morDs, -1, 1);
-            device.setCapacityInKB(size);
-
+            // Reconfigure worker VM with datadisk
+            VirtualDevice device = VmwareHelper.prepareDiskDevice(workerVmMo, null, -1, disks, morDs, -1, 1);
             deviceConfigSpec.setDevice(device);
             deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
             vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
             workerVmMo.configureVm(vmConfigSpec);
+
+            // Write OVF descriptor file
+            OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams();
             String deviceId = File.separator + workerVmMo.getMor().getValue() + File.separator + "VirtualIDEController0:0";
-            bSuccess = true;
             OvfFile ovfFile = new OvfFile();
             ovfFile.setPath(diskFileName);
             ovfFile.setDeviceId(deviceId);
-            ovfFile.setSize(size);
-            // write OVF descriptor file
-            OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams();
+            ovfFile.setSize(fileSize);
+            ovfFile.setCapacity(diskCapacity);
             ovfDescParams.getOvfFiles().add(ovfFile);
-            ManagedObjectReference morOvf = context.getServiceContent().getOvfManager();
             OvfCreateDescriptorResult ovfCreateDescriptorResult = context.getService().createDescriptor(morOvf, workerVmMo.getMor(), ovfDescParams);
 
-            String ovfPath = dir + File.separator + ovfName + ".ovf";
-            FileWriter out = new FileWriter(ovfPath);
-            out.write(ovfCreateDescriptorResult.getOvfDescriptor());
-            out.close();
-        } finally {
-            if (!bSuccess) {
-                workerVmMo.detachAllDisks();
-                workerVmMo.destroy();
+            String ovfPath = templatePath + File.separator + ovfName + ".ovf";
+            try {
+                FileWriter out = new FileWriter(ovfPath);
+                out.write(ovfCreateDescriptorResult.getOvfDescriptor());
+                out.close();
+            } catch (Exception e) {
+                throw e;
             }
+        } finally {
+            workerVmMo.detachAllDisks();
+            workerVmMo.destroy();
         }
     }
 


[12/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. List templates - Add filter parent template id


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/a6fd25ac
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/a6fd25ac
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/a6fd25ac

Branch: refs/heads/multiple-disk-ova
Commit: a6fd25ac3f6a4c382a9954360dd3afcdd27e1878
Parents: 6a38441
Author: Likitha Shetty <li...@citrix.com>
Authored: Wed Apr 30 11:46:13 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:52:20 2014 +0530

----------------------------------------------------------------------
 api/src/org/apache/cloudstack/api/ApiConstants.java      |  1 +
 .../api/command/user/template/ListTemplatesCmd.java      |  7 +++++++
 server/src/com/cloud/api/query/QueryManagerImpl.java     | 11 ++++++++---
 3 files changed, 16 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a6fd25ac/api/src/org/apache/cloudstack/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 7ece5d9..eb4ab12 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -177,6 +177,7 @@ public class ApiConstants {
     public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor";
     public static final String PARAMS = "params";
     public static final String PARENT_DOMAIN_ID = "parentdomainid";
+    public static final String PARENT_TEMPLATE_ID = "parenttemplateid";
     public static final String PASSWORD = "password";
     public static final String NEW_PASSWORD = "new_password";
     public static final String PASSWORD_ENABLED = "passwordenabled";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a6fd25ac/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
index 7a2a158..d257f60 100644
--- a/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
@@ -71,6 +71,9 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd {
     @Parameter(name=ApiConstants.SHOW_REMOVED, type=CommandType.BOOLEAN, description="show removed templates as well")
     private Boolean showRemoved;
 
+    @Parameter(name = ApiConstants.PARENT_TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list datadisk templates by parent template id",
+            since = "4.4")
+    private Long parentTemplateId;
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -99,6 +102,10 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd {
         return (showRemoved != null ? showRemoved : false);
     }
 
+    public Long getParentTemplateId() {
+        return parentTemplateId;
+    }
+
     public boolean listInReadyState() {
 
         Account account = CallContext.current().getCallingAccount();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a6fd25ac/server/src/com/cloud/api/query/QueryManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java
index 8e020fc..96f759b 100644
--- a/server/src/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/com/cloud/api/query/QueryManagerImpl.java
@@ -2812,6 +2812,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
         Long id = cmd.getId();
         Map<String, String> tags = cmd.getTags();
         boolean showRemovedTmpl = cmd.getShowRemoved();
+        Long parentTemplateId = cmd.getParentTemplateId();
         Account caller = CallContext.current().getCallingAccount();
 
         // TODO: listAll flag has some conflicts with TemplateFilter parameter
@@ -2841,7 +2842,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
         return searchForTemplatesInternalIAM(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null,
                 cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType, showDomr,
-                cmd.listInReadyState(), permittedDomains, permittedAccounts, permittedResources, isRecursive, caller, listProjectResourcesCriteria, tags, showRemovedTmpl);
+                cmd.listInReadyState(), permittedDomains, permittedAccounts, permittedResources, isRecursive, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, parentTemplateId);
     }
 
     // Temporarily disable this method which used IAM model to do template list
@@ -2850,7 +2851,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
             Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady,
             List<Long> permittedDomains, List<Long> permittedAccounts, List<Long> permittedResources, boolean isRecursive, Account caller,
             ListProjectResourcesCriteria listProjectResourcesCriteria,
-            Map<String, String> tags, boolean showRemovedTmpl) {
+            Map<String, String> tags, boolean showRemovedTmpl, Long parentTemplateId) {
 
         // check if zone is configured, if not, just return empty list
         List<HypervisorType> hypers = null;
@@ -3047,6 +3048,10 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
             sc.addAnd("dataCenterId", SearchCriteria.Op.SC, zoneSc);
         }
 
+        if (parentTemplateId != null) {
+            sc.addAnd("parentTemplateId", SearchCriteria.Op.EQ, parentTemplateId);
+        }
+
         // don't return removed template, this should not be needed since we
         // changed annotation for removed field in TemplateJoinVO.
         // sc.addAnd("removed", SearchCriteria.Op.NULL);
@@ -3546,7 +3551,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
         return searchForTemplatesInternalIAM(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true,
                 cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType, true,
-                cmd.listInReadyState(), permittedDomains, permittedAccounts, permittedResources, isRecursive, caller, listProjectResourcesCriteria, tags, showRemovedISO);
+                cmd.listInReadyState(), permittedDomains, permittedAccounts, permittedResources, isRecursive, caller, listProjectResourcesCriteria, tags, showRemovedISO, null);
     }
 
     @Override


[09/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. Deploy Virtual Machine - If datadisk templates are specified using parameter 'datadisktemplatetodiskofferinglist', then create virtual machine with additional volumes attached


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/ad8033f9
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/ad8033f9
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/ad8033f9

Branch: refs/heads/multiple-disk-ova
Commit: ad8033f9807ca06f36ee85750a697fa9b4f284c7
Parents: 5db1bca
Author: Likitha Shetty <li...@citrix.com>
Authored: Mon Mar 10 19:40:05 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:51:52 2014 +0530

----------------------------------------------------------------------
 api/src/com/cloud/vm/DiskProfile.java           |  4 ++
 api/src/com/cloud/vm/UserVmService.java         | 22 +++++++--
 .../org/apache/cloudstack/api/ApiConstants.java |  1 +
 .../cloudstack/api/ResponseGenerator.java       |  2 +
 .../api/command/user/vm/DeployVMCmd.java        | 41 +++++++++++++++--
 .../src/com/cloud/vm/VirtualMachineManager.java |  4 +-
 .../service/VolumeOrchestrationService.java     |  5 ++-
 .../service/api/OrchestrationService.java       |  4 +-
 .../com/cloud/vm/VirtualMachineManagerImpl.java | 27 ++++++++---
 .../engine/orchestration/CloudOrchestrator.java | 19 ++++++--
 .../orchestration/VolumeOrchestrator.java       |  8 +++-
 .../resource/VmwareStorageProcessor.java        |  6 ++-
 server/src/com/cloud/api/ApiResponseHelper.java |  5 +++
 .../cloud/network/as/AutoScaleManagerImpl.java  |  6 +--
 server/src/com/cloud/vm/UserVmManagerImpl.java  | 47 +++++++++++++++-----
 15 files changed, 166 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/api/src/com/cloud/vm/DiskProfile.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/DiskProfile.java b/api/src/com/cloud/vm/DiskProfile.java
index a37f7aa..d909774 100644
--- a/api/src/com/cloud/vm/DiskProfile.java
+++ b/api/src/com/cloud/vm/DiskProfile.java
@@ -139,6 +139,10 @@ public class DiskProfile {
         return templateId;
     }
 
+    public void setTemplateId(Long templateId) {
+        this.templateId = templateId;
+    }
+
     /**
      * @return disk offering id that the disk is based on.
      */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/api/src/com/cloud/vm/UserVmService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index af4e1d3..c4bca71 100755
--- a/api/src/com/cloud/vm/UserVmService.java
+++ b/api/src/com/cloud/vm/UserVmService.java
@@ -50,6 +50,7 @@ import com.cloud.exception.VirtualMachineMigrationException;
 import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Network.IpAddresses;
+import com.cloud.offering.DiskOffering;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.storage.StoragePool;
 import com.cloud.template.VirtualMachineTemplate;
@@ -185,6 +186,11 @@ public interface UserVmService {
      * @param memory
      * @param cpuNumber
      * @param customId
+     * @param dataDiskTemplateToDiskOfferingMap
+     *            - Datadisk template to Disk offering Map
+     *             an optional parameter that creates additional data disks for the virtual machine
+     *             For each of the templates in the map, a data disk will be created from the corresponding
+     *             disk offering obtained from the map
      * @return UserVm object if successful.
      *
      * @throws InsufficientCapacityException
@@ -199,7 +205,7 @@ public interface UserVmService {
     UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList,
         Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
         String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard,
-        List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId) throws InsufficientCapacityException,
+        List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException,
         ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -258,6 +264,11 @@ public interface UserVmService {
      * @param memory
      * @param cpuNumber
      * @param customId
+     * @param dataDiskTemplateToDiskOfferingMap
+     *            - Datadisk template to Disk offering Map
+     *             an optional parameter that creates additional data disks for the virtual machine
+     *             For each of the templates in the map, a data disk will be created from the corresponding
+     *             disk offering obtained from the map
      * @return UserVm object if successful.
      *
      * @throws InsufficientCapacityException
@@ -272,7 +283,7 @@ public interface UserVmService {
     UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
         List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
         HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
-        List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId) throws InsufficientCapacityException,
+        List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException,
         ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 
     /**
@@ -329,6 +340,11 @@ public interface UserVmService {
      * @param memory
      * @param cpuNumber
      * @param customId
+     * @param dataDiskTemplateToDiskOfferingMap
+     *            - Datadisk template to Disk offering Map
+     *             an optional parameter that creates additional data disks for the virtual machine
+     *             For each of the templates in the map, a data disk will be created from the corresponding
+     *             disk offering obtained from the map
      * @return UserVm object if successful.
      *
      * @throws InsufficientCapacityException
@@ -343,7 +359,7 @@ public interface UserVmService {
     UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner,
         String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
         String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
-        Map<String, String> customParameters, String customId)
+        Map<String, String> customParameters, String customId, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap)
 
         throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/api/src/org/apache/cloudstack/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index a3faca7..7ece5d9 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -67,6 +67,7 @@ public class ApiConstants {
     public static final String MIN_IOPS = "miniops";
     public static final String MAX_IOPS = "maxiops";
     public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve";
+    public static final String DATADISKTEMPLATE_TO_DISKOFFERING_LIST = "datadisktemplatetodiskofferinglist";
     public static final String DESCRIPTION = "description";
     public static final String DESTINATION_ZONE_ID = "destzoneid";
     public static final String DETAILS = "details";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/api/src/org/apache/cloudstack/api/ResponseGenerator.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
index 10fb6df..3cb656d 100644
--- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java
+++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java
@@ -281,6 +281,8 @@ public interface ResponseGenerator {
 
     Host findHostById(Long hostId);
 
+    DiskOffering findDiskOfferingById(Long diskOfferingId);
+
     VpnUsersResponse createVpnUserResponse(VpnUser user);
 
     RemoteAccessVpnResponse createRemoteAccessVpnResponse(RemoteAccessVpn vpn);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 0adc57b..c59cea2 100755
--- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -190,6 +190,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
     @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin })
     private String deploymentPlanner;
 
+    @Parameter(name = ApiConstants.DATADISKTEMPLATE_TO_DISKOFFERING_LIST, type = CommandType.MAP, since = "4.4", description = "datadisk template to disk-offering mapping;" +
+            " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter")
+    private Map dataDiskTemplateToDiskOfferingList;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -393,6 +397,37 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
         }
     }
 
+    public Map<Long, DiskOffering> getDataDiskTemplateToDiskOfferingMap() {
+        if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) {
+            throw new InvalidParameterValueException("diskofferingid paramter can't be specified along with datadisktemplatetodiskofferinglist parameter");
+        }
+        HashMap<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap = new HashMap<Long, DiskOffering>();
+        if (dataDiskTemplateToDiskOfferingList != null && !dataDiskTemplateToDiskOfferingList.isEmpty()) {
+            Collection dataDiskTemplatesCollection = dataDiskTemplateToDiskOfferingList.values();
+            Iterator iter = dataDiskTemplatesCollection.iterator();
+            while (iter.hasNext()) {
+                HashMap<String, String> dataDiskTemplates = (HashMap<String, String>)iter.next();
+                Long dataDiskTemplateId;
+                DiskOffering dataDiskOffering = null;
+                VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid"));
+                if (dataDiskTemplate == null) {
+                    dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid"));
+                    if (dataDiskTemplate == null)
+                        throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid"));
+                }
+                dataDiskTemplateId = dataDiskTemplate.getId();
+                dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid"));
+                if (dataDiskOffering == null) {
+                    dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid"));
+                    if (dataDiskOffering == null)
+                        throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid"));
+                }
+                dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering);
+            }
+        }
+        return dataDiskTemplateToDiskOfferingMap;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -585,13 +620,13 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
                 } else {
                     vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(), owner, name, displayName, diskOfferingId,
                             size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(),
-                            getDetails(), getCustomId());
+                            getDetails(), getCustomId(), getDataDiskTemplateToDiskOfferingMap());
                 }
             } else {
                 if (zone.isSecurityGroupEnabled())  {
                     vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(), owner, name,
                             displayName, diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard,
-                            getAffinityGroupIdList(), getDetails(), getCustomId());
+                            getAffinityGroupIdList(), getDetails(), getCustomId(), getDataDiskTemplateToDiskOfferingMap());
 
                 } else {
                     if (getSecurityGroupIdList() != null && !getSecurityGroupIdList().isEmpty()) {
@@ -599,7 +634,7 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
                     }
                     vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, getNetworkIds(), owner, name, displayName, diskOfferingId, size, group,
                             getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), getDetails(),
-                            getCustomId());
+                            getCustomId(), getDataDiskTemplateToDiskOfferingMap());
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/engine/api/src/com/cloud/vm/VirtualMachineManager.java
----------------------------------------------------------------------
diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
index f070210..a1c3809 100644
--- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java
+++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
@@ -36,6 +36,7 @@ import com.cloud.exception.OperationTimedoutException;
 import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Network;
+import com.cloud.offering.DiskOffering;
 import com.cloud.offering.DiskOfferingInfo;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.storage.StoragePool;
@@ -73,11 +74,12 @@ public interface VirtualMachineManager extends Manager {
      * @param auxiliaryNetworks additional networks to attach the VMs to.
      * @param plan How to deploy the VM.
      * @param hyperType Hypervisor type
+     * @param datadiskTemplateToDiskOfferingMap data disks to be created from datadisk templates and attached to the VM
      * @throws InsufficientCapacityException If there are insufficient capacity to deploy this vm.
      */
     void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, DiskOfferingInfo rootDiskOfferingInfo,
         List<DiskOfferingInfo> dataDiskOfferings, LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, DeploymentPlan plan,
-        HypervisorType hyperType) throws InsufficientCapacityException;
+        HypervisorType hyperType, Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap) throws InsufficientCapacityException;
 
     void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering,
         LinkedHashMap<? extends Network, List<? extends NicProfile>> networkProfiles, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
index df0b5e8..90625f7 100644
--- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
@@ -23,6 +23,7 @@ import java.util.Set;
 
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.config.ConfigKey;
 
 import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.dc.DataCenter;
@@ -45,7 +46,6 @@ import com.cloud.utils.fsm.NoTransitionException;
 import com.cloud.vm.DiskProfile;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
-import org.apache.cloudstack.framework.config.ConfigKey;
 
 /**
  * VolumeOrchestrationService is a PURE orchestration service on CloudStack
@@ -87,7 +87,8 @@ public interface VolumeOrchestrationService {
 
     void destroyVolume(Volume volume);
 
-    DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner);
+    DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template,
+            Account owner, Long deviceId);
 
     VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException;
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
index 93f969f..b259808 100755
--- a/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java
@@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.hypervisor.Hypervisor;
+import com.cloud.offering.DiskOffering;
 import com.cloud.vm.NicProfile;
 
 @Path("orchestration")
@@ -56,6 +57,7 @@ public interface OrchestrationService {
      * @param rootDiskTags tags for the root disk
      * @param networks networks that this VM should join
      * @param rootDiskSize size the root disk in case of templates.
+     * @param dataDiskTemplateDiskOfferingMap disk offerings in case of data disk templates
      * @return VirtualMachineEntity
      */
     @POST
@@ -65,7 +67,7 @@ public interface OrchestrationService {
         @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize,
         @QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
         @QueryParam("network-nic-map") Map<String, NicProfile> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
-        @QueryParam("root-disk-size") Long rootDiskSize) throws InsufficientCapacityException;
+        @QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("datadisktemplate-diskoffering-map") Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap) throws InsufficientCapacityException;
 
     @POST
     VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId,

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
index e15d287..0b07026 100755
--- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -29,6 +29,7 @@ import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.UUID;
@@ -40,6 +41,8 @@ import javax.ejb.Local;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import org.apache.log4j.Logger;
+
 import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -68,7 +71,6 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
-import org.apache.log4j.Logger;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.Listener;
@@ -152,6 +154,7 @@ import com.cloud.network.NetworkModel;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.rules.RulesManager;
+import com.cloud.offering.DiskOffering;
 import com.cloud.offering.DiskOfferingInfo;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.org.Cluster;
@@ -161,6 +164,7 @@ import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.StoragePool;
+import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.Volume;
 import com.cloud.storage.Volume.Type;
 import com.cloud.storage.VolumeVO;
@@ -374,7 +378,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
     @DB
     public void allocate(String vmInstanceName, final VirtualMachineTemplate template, ServiceOffering serviceOffering,
             final DiskOfferingInfo rootDiskOfferingInfo, final List<DiskOfferingInfo> dataDiskOfferings,
-            final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, DeploymentPlan plan, HypervisorType hyperType)
+            final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, DeploymentPlan plan, HypervisorType hyperType,
+                    final Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap)
                     throws InsufficientCapacityException {
 
         VMInstanceVO vm = _vmDao.findVMByInstanceName(vmInstanceName);
@@ -412,7 +417,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
 
                         if (template.getFormat() == ImageFormat.ISO) {
                             volumeMgr.allocateRawVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(),
-                                    rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vmFinal, template, owner);
+                                    rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vmFinal, template, owner, null);
                         } else if (template.getFormat() == ImageFormat.BAREMETAL) {
                             // Do nothing
                         } else {
@@ -423,7 +428,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                         if (dataDiskOfferings != null) {
                             for (DiskOfferingInfo dataDiskOfferingInfo : dataDiskOfferings) {
                                 volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vmFinal.getId(), dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(),
-                                        dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), vmFinal, template, owner);
+                                        dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), vmFinal, template, owner, null);
+                            }
+                        }
+
+                        if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) {
+                            int diskNumber = 1;
+                            for (Entry<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap : datadiskTemplateToDiskOfferingMap.entrySet()) {
+                                DiskOffering diskOffering = dataDiskTemplateToDiskOfferingMap.getValue();
+                                long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024);
+                                VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
+                                volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vmFinal.getId() + "-" + String.valueOf(diskNumber), diskOffering, diskOfferingSize, null, null,
+                                        vmFinal, dataDiskTemplate, owner, Long.valueOf(diskNumber));
+                                diskNumber++;
                             }
                         }
                     }
@@ -437,7 +454,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
     @Override
     public void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering,
             LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException {
-        allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), new ArrayList<DiskOfferingInfo>(), networks, plan, hyperType);
+        allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), new ArrayList<DiskOfferingInfo>(), networks, plan, hyperType, null);
     }
 
     private VirtualMachineGuru getVmGuru(VirtualMachine vm) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
index 2b49954..994bd92 100755
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java
@@ -24,6 +24,7 @@ import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.inject.Inject;
 
@@ -45,6 +46,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Network;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
+import com.cloud.offering.DiskOffering;
 import com.cloud.offering.DiskOfferingInfo;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
@@ -155,7 +157,7 @@ public class CloudOrchestrator implements OrchestrationService {
     @Override
     public VirtualMachineEntity createVirtualMachine(String id, String owner, String templateId, String hostName, String displayName, String hypervisor, int cpu,
         int speed, long memory, Long diskSize, List<String> computeTags, List<String> rootDiskTags, Map<String, NicProfile> networkNicMap, DeploymentPlan plan,
-        Long rootDiskSize) throws InsufficientCapacityException {
+        Long rootDiskSize, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException {
 
         // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks,
         // vmEntityManager);
@@ -233,8 +235,19 @@ public class CloudOrchestrator implements OrchestrationService {
             dataDiskOfferings.add(dataDiskOfferingInfo);
         }
 
+        if (dataDiskTemplateToDiskOfferingMap != null && !dataDiskTemplateToDiskOfferingMap.isEmpty()) {
+            for (Entry<Long, DiskOffering> datadiskTemplateToDiskOffering : dataDiskTemplateToDiskOfferingMap.entrySet()) {
+                DiskOffering diskOffering = datadiskTemplateToDiskOffering.getValue();
+                if (diskOffering == null) {
+                    throw new InvalidParameterValueException("Unable to find disk offering " + vm.getDiskOfferingId());
+                }
+                if (diskOffering.getDiskSize() == 0) { // Custom disk offering is not supported for volumes created from datadisk templates
+                    throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter.");
+                }
+            }
+        }
         _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(templateId)), computeOffering, rootDiskOfferingInfo, dataDiskOfferings, networkIpMap, plan,
-            hypervisorType);
+            hypervisorType, dataDiskTemplateToDiskOfferingMap);
 
         return vmEntity;
     }
@@ -299,7 +312,7 @@ public class CloudOrchestrator implements OrchestrationService {
 
         HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor);
 
-        _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), computeOffering, rootDiskOfferingInfo, new ArrayList<DiskOfferingInfo>(), networkIpMap, plan, hypervisorType);
+        _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), computeOffering, rootDiskOfferingInfo, new ArrayList<DiskOfferingInfo>(), networkIpMap, plan, hypervisorType, null);
 
         return vmEntity;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 6256e25..f453f6b 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -583,7 +583,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
     }
 
     @Override
-    public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner) {
+    public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner, Long deviceId) {
         if (size == null) {
             size = offering.getDiskSize();
         } else {
@@ -608,13 +608,17 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
             vol.setInstanceId(vm.getId());
         }
 
-        if (type.equals(Type.ROOT)) {
+        if (deviceId != null) {
+            vol.setDeviceId(deviceId);
+        } else if (type.equals(Type.ROOT)) {
             vol.setDeviceId(0l);
         } else {
             vol.setDeviceId(1l);
         }
         if (template.getFormat() == ImageFormat.ISO) {
             vol.setIsoId(template.getId());
+        } else if(template.getTemplateType().equals(Storage.TemplateType.DATADISK)) {
+            vol.setTemplateId(template.getId());
         }
         // display flag matters only for the User vms
         if (vm.getType() == VirtualMachine.Type.User) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
index ff893b2..131fe0a 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
@@ -456,7 +456,7 @@ public class VmwareStorageProcessor implements StorageProcessor {
                 vmMo = new ClusterMO(context, morCluster).findVmOnHyperHost(vmdkName);
                 assert (vmMo != null);
 
-                vmdkFileBaseName = vmMo.getVmdkFileBaseNames().get(0); // TO-DO: Support for base template containing multiple disks
+                vmdkFileBaseName = vmMo.getVmdkFileBaseNames().get(0);
                 s_logger.info("Move volume out of volume-wrapper VM ");
                 String[] vmwareLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkFileBaseName, VmwareStorageLayoutType.VMWARE, !_fullCloneFlag);
                 String[] legacyCloudStackLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkFileBaseName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, !_fullCloneFlag);
@@ -471,7 +471,9 @@ public class VmwareStorageProcessor implements StorageProcessor {
                 vmMo.destroy();
 
                 String srcFile = dsMo.getDatastorePath(vmdkName, true);
-                dsMo.deleteFile(srcFile, dcMo.getMor(), true);
+                if (dsMo.folderExists(String.format("[%s]", dsMo.getName()), vmdkName)) {
+                    dsMo.deleteFolder(srcFile, dcMo.getMor());
+                }
             }
             // restoreVM - move the new ROOT disk into corresponding VM folder
             String vmInternalCSName = volume.getVmName();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 250f5a9..6d78d62 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -1259,6 +1259,11 @@ public class ApiResponseHelper implements ResponseGenerator {
     }
 
     @Override
+    public DiskOfferingVO findDiskOfferingById(Long diskOfferingId) {
+        return ApiDBUtils.findDiskOfferingById(diskOfferingId);
+    }
+
+    @Override
     public VpnUsersResponse createVpnUserResponse(VpnUser vpnUser) {
         VpnUsersResponse vpnResponse = new VpnUsersResponse();
         vpnResponse.setId(vpnUser.getUuid());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
index 09c6694..044eee5 100644
--- a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
+++ b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java
@@ -1331,18 +1331,18 @@ public class AutoScaleManagerImpl<Type> extends ManagerBase implements AutoScale
                 vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" +
                     getCurrentTimeStampString(),
                     "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null,
-                    null, true, null, null, null, null);
+                    null, true, null, null, null, null, null);
             } else {
                 if (zone.isSecurityGroupEnabled()) {
                     vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, null, null,
                         owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(),
                         "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null,
-                        null, null, true, null, null, null, null);
+                        null, null, true, null, null, null, null, null);
 
                 } else {
                     vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" +
                         getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(),
-                        null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null);
+                        null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null);
 
                 }
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ad8033f9/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 3d262b7..9a0d2a1 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -183,6 +183,7 @@ import com.cloud.network.security.dao.SecurityGroupDao;
 import com.cloud.network.security.dao.SecurityGroupVMMapDao;
 import com.cloud.network.vpc.VpcManager;
 import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.offering.DiskOffering;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.offering.NetworkOffering.Availability;
 import com.cloud.offering.ServiceOffering;
@@ -2274,7 +2275,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
     public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> securityGroupIdList,
             Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
             String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
-            Map<String, String> customParametes, String customId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
+            Map<String, String> customParametes, String customId, Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
             StorageUnavailableException, ResourceAllocationException {
 
         Account caller = CallContext.current().getCallingAccount();
@@ -2318,7 +2319,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod,
-                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId);
+                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, datadiskTemplateToDiskOfferringMap);
 
     }
 
@@ -2327,7 +2328,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
     public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList,
             List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
             HTTPMethod httpmethod, String userData, String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
-            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId) throws InsufficientCapacityException, ConcurrentOperationException,
+            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap) throws InsufficientCapacityException, ConcurrentOperationException,
             ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
 
         Account caller = CallContext.current().getCallingAccount();
@@ -2425,7 +2426,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod,
-                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId);
+                userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, datadiskTemplateToDiskOfferringMap);
     }
 
     @Override
@@ -2433,7 +2434,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
     public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Long> networkIdList, Account owner,
             String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
             String sshKeyPair, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List<Long> affinityGroupIdList,
-            Map<String, String> customParametrs, String customId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
+            Map<String, String> customParametrs, String customId, Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
             StorageUnavailableException, ResourceAllocationException {
 
         Account caller = CallContext.current().getCallingAccount();
@@ -2517,7 +2518,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         }
 
         return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, userData,
-                sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId);
+                sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, datadiskTemplateToDiskOfferringMap);
     }
 
     public void checkNameForRFCCompliance(String name) {
@@ -2531,7 +2532,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
     protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate tmplt, String hostName, String displayName, Account owner,
             Long diskOfferingId, Long diskSize, List<NetworkVO> networkList, List<Long> securityGroupIdList, String group, HTTPMethod httpmethod, String userData,
             String sshKeyPair, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard,
-            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId) throws InsufficientCapacityException, ResourceUnavailableException,
+            List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferringMap) throws InsufficientCapacityException, ResourceUnavailableException,
             ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
 
         _accountMgr.checkAccess(caller, null, owner);
@@ -2602,6 +2603,32 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2));
         _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, size);
 
+        if (dataDiskTemplateToDiskOfferringMap != null && !dataDiskTemplateToDiskOfferringMap.isEmpty()) {
+            for (Entry<Long, DiskOffering> datadiskTemplateToDiskOffering : dataDiskTemplateToDiskOfferringMap.entrySet()) {
+                VMTemplateVO dataDiskTemplate = _templateDao.findById(datadiskTemplateToDiskOffering.getKey());
+                DiskOffering dataDiskOffering = datadiskTemplateToDiskOffering.getValue();
+
+                if (dataDiskTemplate == null || (!dataDiskTemplate.getTemplateType().equals(TemplateType.DATADISK)) &&
+                        (dataDiskTemplate.getState().equals(VirtualMachineTemplate.State.Active))) {
+                    throw new InvalidParameterValueException("Invalid template id specified for Datadisk template" + datadiskTemplateToDiskOffering.getKey());
+                }
+                if (!dataDiskTemplate.getParentTemplateId().equals(template.getId())) {
+                    throw new InvalidParameterValueException("Invalid Datadisk template. Specified Datadisk template " + datadiskTemplateToDiskOffering.getKey()
+                            + " doesn't belong to template " + template.getId());
+                }
+                if (dataDiskOffering == null) {
+                    throw new InvalidParameterValueException("Invalid disk offering id specified" + datadiskTemplateToDiskOffering.getValue());
+                }
+                _templateDao.loadDetails(dataDiskTemplate);
+                if (dataDiskOffering.isCustomized()) {
+                    throw new InvalidParameterValueException("Invalid disk offering id specified" + datadiskTemplateToDiskOffering.getValue() + ". Custom Disk offerings are not" +
+                            " supported for Datadisk templates");
+                }
+                _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, 1);
+                _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, dataDiskOffering.getDiskSize());
+            }
+        }
+
         // verify security group ids
         if (securityGroupIdList != null) {
             for (Long securityGroupId : securityGroupIdList) {
@@ -2785,7 +2812,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         }
 
         UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, hypervisor, caller, isDisplayVm, keyboard, accountId,
-                offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters);
+                offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dataDiskTemplateToDiskOfferringMap);
 
         // Assign instance to the group
         try {
@@ -2845,7 +2872,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
             final Long diskOfferingId, final Long diskSize, final String userData, final HypervisorType hypervisor, final Account caller, final Boolean isDisplayVm,
             final String keyboard, final long accountId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey,
             final LinkedHashMap<String, NicProfile> networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType,
-            final Map<String, String> customParameters) throws InsufficientCapacityException {
+            final Map<String, String> customParameters, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException {
         return Transaction.execute(new TransactionCallbackWithException<UserVmVO, InsufficientCapacityException>() {
             @Override
             public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException {
@@ -2954,7 +2981,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                             networkNicMap, plan);
                 } else {
                     _orchSrvc.createVirtualMachine(vm.getUuid(), Long.toString(owner.getAccountId()), Long.toString(template.getId()), hostName, displayName, hypervisor.name(),
-                            offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize);
+                            offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize, dataDiskTemplateToDiskOfferingMap);
                 }
 
                 if (s_logger.isDebugEnabled()) {


[13/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. Marvin test to verify support for OVA containing multiple disk


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/3532ffe5
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/3532ffe5
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/3532ffe5

Branch: refs/heads/multiple-disk-ova
Commit: 3532ffe5af5553e558cfe6de2fef059b8d5e100e
Parents: a6fd25a
Author: Likitha Shetty <li...@citrix.com>
Authored: Fri May 2 09:33:14 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:52:29 2014 +0530

----------------------------------------------------------------------
 .../test_ova_templates_with_multiple_disks.py   | 306 +++++++++++++++++++
 tools/marvin/marvin/lib/base.py                 |   8 +
 2 files changed, 314 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3532ffe5/test/integration/component/test_ova_templates_with_multiple_disks.py
----------------------------------------------------------------------
diff --git a/test/integration/component/test_ova_templates_with_multiple_disks.py b/test/integration/component/test_ova_templates_with_multiple_disks.py
new file mode 100644
index 0000000..adb948c
--- /dev/null
+++ b/test/integration/component/test_ova_templates_with_multiple_disks.py
@@ -0,0 +1,306 @@
+# 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.
+""" P1 tests for OVA templates with multiple disks
+"""
+import marvin
+from nose.plugins.attrib import attr
+from marvin.cloudstackTestCase import *
+from marvin.cloudstackAPI import *
+from marvin.integration.lib.utils import *
+from marvin.integration.lib.base import *
+from marvin.integration.lib.common import *
+import urllib
+from random import random
+import time
+
+
+class Services:
+    """Test OVA template with mutiple disks
+    """
+
+    def __init__(self):
+        self.services = {
+                        "account": {
+                                    "email": "test@test.com",
+                                    "firstname": "Test",
+                                    "lastname": "User",
+                                    "username": "test",
+                                    "password": "password",
+                         },
+                         "service_offering": {
+                                    "name": "Tiny Instance",
+                                    "displaytext": "Tiny Instance",
+                                    "cpunumber": 1,
+                                    "cpuspeed": 100, # in MHz
+                                    "memory": 128, # In MBs
+                        },
+                        "disk_offering": {
+                                    "displaytext": "Small",
+                                    "name": "Small",
+                                    "disksize": 1
+                        },
+                        "virtual_machine": {
+                                    "displayname": "testVM",
+                                    "hypervisor": 'VMware',
+                                    "protocol": 'TCP',
+                                    "ssh_port": 22,
+                                    "username": "root",
+                                    "password": "password",
+                                    "privateport": 22,
+                                    "publicport": 22,
+                         },
+                        "templates": {
+                            0: {
+                                "displaytext": "Template with multiple disks",
+                                "name": "Template with multiple disks",
+                                "ostype": 'CentOS 5.3 (64-bit)',
+                                "url": "http://10.147.28.7/templates/multipledisk.ova",
+                                "hypervisor": 'VMware',
+                                "format": 'OVA',
+                                "isfeatured": True,
+                                "ispublic": True,
+                                "isextractable": False,
+                                },
+                        },
+                        "sleep": 60,
+                        "timeout": 10,
+                     }
+
+
+class TestOVATemplateWithMupltipleDisks(cloudstackTestCase):
+
+    def setUp(self):
+
+        self.apiclient = self.testClient.getApiClient()
+        self.dbclient = self.testClient.getDbConnection()
+        self.cleanup = []
+        return
+
+    def tearDown(self):
+        try:
+            # Clean up, terminate the created templates
+            cleanup_resources(self.apiclient, self.cleanup)
+
+        except Exception as e:
+            raise Exception("Warning: Exception during cleanup : %s" % e)
+        return
+
+    @classmethod
+    def setUpClass(cls):
+        cls.services = Services().services
+        cls.api_client = super(TestOVATemplateWithMupltipleDisks, cls).getClsTestClient().getApiClient()
+        cls.api_client.hypervisor = 'VMware'
+
+        # Get Zone, Domain and templates
+        cls.zone = get_zone(cls.api_client, cls.services)
+        cls.domain = get_domain(cls.api_client, cls.services)
+        cls.services['mode'] = cls.zone.networktype
+        cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+
+        cls.service_offering = ServiceOffering.create(
+                                            cls.api_client,
+                                            cls.services["service_offering"]
+                                            )
+        cls.disk_offering = DiskOffering.create(
+                                    cls.api_client,
+                                    cls.services["disk_offering"]
+                                    )
+        cls.account = Account.create(
+                            cls.api_client,
+                            cls.services["account"],
+                            domainid=cls.domain.id
+                            )
+        cls.services["account"] = cls.account.name
+
+        cls._cleanup = [
+                        cls.account,
+                        cls.service_offering,
+                        cls.disk_offering,
+                        ]
+        return
+
+    @classmethod
+    def tearDownClass(cls):
+        try:
+            cls.api_client = super(TestOVATemplateWithMupltipleDisks, cls).getClsTestClient().getApiClient()
+            # Cleanup resources used
+            cleanup_resources(cls.api_client, cls._cleanup)
+
+        except Exception as e:
+            raise Exception("Warning: Exception during cleanup : %s" % e)
+
+        return
+
+    @attr(tags = ["vmware"])
+    def test_01_template_with_multiple_disks(self):
+        """Test template with 1 data disks
+        """
+        # Validate the following:
+        # 1. Register a template in OVA format that contains 1 data disks
+        # 2. Verify template is in READY state
+        # 3. Veriy 1 additonal Datadisk Template got created
+        # 3. Deploy a VM from the registered template and 1 datadisk template
+        # 4. Verify VM is in Running state
+        # 5. Verify an additional data disk attached to the VM
+
+        # Register new template
+        template = Template.register(
+                                        self.apiclient,
+                                        self.services["templates"][0],
+                                        zoneid=self.zone.id,
+                                        account=self.account.name,
+                                        domainid=self.account.domainid
+                                        )
+        self.debug(
+                "Registered a template of format: %s with ID: %s" % (
+                                                                self.services["templates"][0]["format"],
+                                                                template.id
+                                                                ))
+        # Wait for template to download
+        template.download(self.apiclient)
+        self.cleanup.append(template)
+
+        # Wait for template status to be changed across
+        time.sleep(self.services["sleep"])
+        timeout = self.services["timeout"]
+        while True:
+            list_template_response = list_templates(
+                                    self.apiclient,
+                                    templatefilter='all',
+                                    id=template.id,
+                                    zoneid=self.zone.id,
+                                    account=self.account.name,
+                                    domainid=self.account.domainid
+                                    )
+            if isinstance(list_template_response, list):
+                break
+            elif timeout == 0:
+                raise Exception("List template failed!")
+
+            time.sleep(5)
+            timeout = timeout - 1
+        # Verify template response to check if template was successfully added
+        self.assertEqual(
+                        isinstance(list_template_response, list),
+                        True,
+                        "Check for list template response return valid data"
+                        )
+
+        self.assertNotEqual(
+                            len(list_template_response),
+                            0,
+                            "Check template available in List Templates"
+                        )
+
+        template_response = list_template_response[0]
+        self.assertEqual(
+                            template_response.isready,
+                            True,
+                            "Template state is not ready, it is %s" % template_response.isready
+                        )
+
+        time.sleep(120)
+        # Veriy 1 additonal Datadisk Templates got created
+        list_datadisk_template_response = list_templates(
+                                    self.apiclient,
+                                    templatefilter='all',
+                                    parenttemplateid=template.id,
+                                    zoneid=self.zone.id,
+                                    account=self.account.name,
+                                    domainid=self.account.domainid
+                                    )
+
+        self.assertEqual(
+                        isinstance(list_datadisk_template_response, list),
+                        True,
+                        "Check for datadisk list template response return valid data"
+                        )
+
+        self.assertNotEqual(
+                            len(list_datadisk_template_response),
+                            0,
+                            "Check datadisk template available in List Templates"
+                        )
+
+        datadisk_template_response = list_datadisk_template_response[0]
+        self.assertEqual(
+                            datadisk_template_response.isready,
+                            True,
+                            "Datadisk template state is not ready, it is %s" % datadisk_template_response.isready
+                        )
+
+        # Deploy new virtual machine using template
+        virtual_machine = VirtualMachine.create(
+                                    self.apiclient,
+                                    self.services["virtual_machine"],
+                                    templateid=template.id,
+                                    accountid=self.account.name,
+                                    domainid=self.account.domainid,
+                                    serviceofferingid=self.service_offering.id,
+                                    mode=self.services["mode"],
+                                    datadisktemplateid=datadisk_template_response.id,
+                                    datadiskofferingid=self.disk_offering.id
+                                    )
+        self.debug("Creating an instance with template ID: %s" % template.id)
+        vm_response = list_virtual_machines(
+                                        self.apiclient,
+                                        id=virtual_machine.id,
+                                        account=self.account.name,
+                                        domainid=self.account.domainid
+                                        )
+        self.assertEqual(
+                             isinstance(vm_response, list),
+                             True,
+                             "Check for list VMs response after VM deployment"
+                             )
+        # Verify VM response to check if VM deployment was successful
+        self.assertNotEqual(
+                            len(vm_response),
+                            0,
+                            "Check VMs available in List VMs response"
+                        )
+        vm = vm_response[0]
+        self.assertEqual(
+                            vm.state,
+                            'Running',
+                            "Check the state of VM created from Template"
+                        )
+
+        # Check 1 DATA volume is attached to the VM
+        list_volume_response = list_volumes(
+                                    self.apiclient,
+                                    virtualmachineid=vm.id,
+                                    type='DATADISK',
+                                    listall=True
+                                    )
+        self.assertNotEqual(
+            list_volume_response,
+            None,
+            "Check if additinal data volume is attached to VM %s "
+        )
+        self.assertEqual(
+            isinstance(list_volume_response, list),
+            True,
+            "Check list volumes response for valid list"
+        )
+        self.assertEqual(
+            len(list_volume_response),
+            1,
+            "Additional DATA volume attached to the VM %s. Expected %s" % (len(list_volume_response), 1)
+        )
+
+        return

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3532ffe5/tools/marvin/marvin/lib/base.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 966e8dc..82e17b6 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -329,6 +329,7 @@ class VirtualMachine:
                     diskofferingid=None, affinitygroupnames=None, affinitygroupids=None, group=None,
                     hostid=None, keypair=None, ipaddress=None, mode='default', method='GET',hypervisor=None,
                     customcpunumber=None, customcpuspeed=None, custommemory=None, rootdisksize=None):
+                    datadisktemplateid=None, datadiskofferingid=None):
         """Create the instance"""
 
         cmd = deployVirtualMachine.deployVirtualMachineCmd()
@@ -433,6 +434,13 @@ class VirtualMachine:
         if group:
             cmd.group = group
 
+        if datadisktemplateid is not None and datadiskofferingid is not None:
+            cmd.datadisktemplatetodiskofferinglist = []
+            cmd.datadisktemplatetodiskofferinglist.append({
+                 'datadisktemplateid': datadisktemplateid,
+                 'diskofferingid': datadiskofferingid
+                 })
+
         #program default access to ssh
         if mode.lower() == 'basic':
             cls.ssh_access_group(apiclient, cmd)


[05/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757 Support OVA with multiple volumes

Fix device id in worker vm.

Signed-off-by: Sateesh Chodapuneedi <sa...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/e467d11d
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/e467d11d
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/e467d11d

Branch: refs/heads/multiple-disk-ova
Commit: e467d11d6fddb58177393a6f5355b463f070150a
Parents: 87352b8
Author: Sateesh Chodapuneedi <sa...@apache.org>
Authored: Thu Mar 13 11:28:29 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 13:07:38 2014 +0530

----------------------------------------------------------------------
 .../src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e467d11d/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index 8f64a03..e17b305 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -1356,10 +1356,11 @@ public class HypervisorHostHelper {
             deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
             vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
             workerVmMo.configureVm(vmConfigSpec);
+            String deviceId = File.separator + workerVmMo.getMor().getValue() + File.separator + "VirtualIDEController0:0";
             bSuccess = true;
             OvfFile ovfFile = new OvfFile();
             ovfFile.setPath(diskFileName);
-            ovfFile.setDeviceId("1000");
+            ovfFile.setDeviceId(deviceId);
             ovfFile.setSize(size);
             // write OVF descriptor file
             OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams();


[08/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. List templates - Include Datadisk templates in listTemplates response.
And for Datadisk templates include reference to parent template that it belongs to.


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/5db1bca6
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/5db1bca6
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/5db1bca6

Branch: refs/heads/multiple-disk-ova
Commit: 5db1bca606ca0f93452c49a362564552771709eb
Parents: 902f231
Author: Likitha Shetty <li...@citrix.com>
Authored: Tue Apr 22 11:36:03 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:51:31 2014 +0530

----------------------------------------------------------------------
 .../api/response/TemplateResponse.java          |   8 ++
 .../com/cloud/storage/dao/VMTemplateDao.java    |   2 +
 .../cloud/storage/dao/VMTemplateDaoImpl.java    |  19 +++-
 .../storage/image/store/TemplateObject.java     |   5 +
 .../storage/image/TemplateEntityImpl.java       |   6 ++
 .../api/query/dao/TemplateJoinDaoImpl.java      |   4 +
 .../com/cloud/api/query/vo/TemplateJoinVO.java  |  14 +++
 setup/db/db/schema-440to450.sql                 | 103 +++++++++++++++++++
 8 files changed, 159 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/TemplateResponse.java b/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
index 3e21043..6177513 100644
--- a/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
@@ -187,6 +187,10 @@ public class TemplateResponse extends BaseResponse implements ControlledViewEnti
     @Param(description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory")
     private Boolean isDynamicallyScalable;
 
+    @SerializedName("parenttemplateid")
+    @Param(description = "if Datadisk template, then id of the root disk template this template belongs to")
+    private String parentTemplateId;
+
     public TemplateResponse() {
         //  zones = new LinkedHashSet<TemplateZoneResponse>();
         tags = new LinkedHashSet<ResourceTagResponse>();
@@ -361,4 +365,8 @@ public class TemplateResponse extends BaseResponse implements ControlledViewEnti
     public String getZoneId() {
         return zoneId;
     }
+
+    public void setParentTemplateId(String parentTemplateId) {
+        this.parentTemplateId = parentTemplateId;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/engine/schema/src/com/cloud/storage/dao/VMTemplateDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/dao/VMTemplateDao.java b/engine/schema/src/com/cloud/storage/dao/VMTemplateDao.java
index 2b815d8..44ffcbe 100755
--- a/engine/schema/src/com/cloud/storage/dao/VMTemplateDao.java
+++ b/engine/schema/src/com/cloud/storage/dao/VMTemplateDao.java
@@ -75,4 +75,6 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long> {
     void loadDetails(VMTemplateVO tmpl);
 
     void saveDetails(VMTemplateVO tmpl);
+
+    List<VMTemplateVO> listByParentTemplatetId(long parentTemplatetId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java
index 401a4a2..c96e2b1 100755
--- a/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java
+++ b/engine/schema/src/com/cloud/storage/dao/VMTemplateDaoImpl.java
@@ -28,11 +28,12 @@ import javax.ejb.Local;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
+import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
+
 import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.domain.dao.DomainDao;
 import com.cloud.host.Host;
@@ -98,6 +99,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
     protected SearchBuilder<VMTemplateVO> NameSearch;
     protected SearchBuilder<VMTemplateVO> TmpltsInZoneSearch;
     protected SearchBuilder<VMTemplateVO> ActiveTmpltSearch;
+    protected SearchBuilder<VMTemplateVO> ParentTemplateIdSearch;
     private SearchBuilder<VMTemplateVO> PublicSearch;
     private SearchBuilder<VMTemplateVO> NameAccountIdSearch;
     private SearchBuilder<VMTemplateVO> PublicIsoSearch;
@@ -278,6 +280,14 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
     }
 
     @Override
+    public List<VMTemplateVO> listByParentTemplatetId(long parentTemplatetId) {
+        SearchCriteria<VMTemplateVO> sc = ParentTemplateIdSearch.create();
+        sc.setParameters("parentTemplateId", parentTemplatetId);
+        sc.setParameters("state", VirtualMachineTemplate.State.Active);
+        return listBy(sc);
+    }
+
+    @Override
     public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
         boolean result = super.configure(name, params);
 
@@ -387,6 +397,11 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
         CountTemplatesByAccount.and("state", CountTemplatesByAccount.entity().getState(), SearchCriteria.Op.EQ);
         CountTemplatesByAccount.done();
 
+        ParentTemplateIdSearch = createSearchBuilder();
+        ParentTemplateIdSearch.and("parentTemplateId", ParentTemplateIdSearch.entity().getParentTemplateId(), SearchCriteria.Op.EQ);
+        ParentTemplateIdSearch.and("state", ParentTemplateIdSearch.entity().getState(), SearchCriteria.Op.EQ);
+        ParentTemplateIdSearch.done();
+
         //        updateStateSearch = this.createSearchBuilder();
         //        updateStateSearch.and("id", updateStateSearch.entity().getId(), Op.EQ);
         //        updateStateSearch.and("state", updateStateSearch.entity().getState(), Op.EQ);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
index 5649028..a28498f 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@ -467,4 +467,9 @@ public class TemplateObject implements TemplateInfo {
     public Class<?> getEntityType() {
         return VirtualMachineTemplate.class;
     }
+
+    @Override
+    public Long getParentTemplateId() {
+        return imageVO.getParentTemplateId();
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/engine/storage/src/org/apache/cloudstack/storage/image/TemplateEntityImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/TemplateEntityImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/TemplateEntityImpl.java
index c1aa8c2..5e038b8 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/TemplateEntityImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/TemplateEntityImpl.java
@@ -290,4 +290,10 @@ public class TemplateEntityImpl implements TemplateEntity {
     public Class<?> getEntityType() {
         return VirtualMachineTemplate.class;
     }
+
+    @Override
+    public Long getParentTemplateId() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
index 80ef0f6..d245d17 100644
--- a/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
@@ -180,6 +180,10 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
         }
         templateResponse.setTemplateTag(template.getTemplateTag());
 
+        if (template.getParentTemplateId() != null) {
+            templateResponse.setParentTemplateId(template.getParentTemplateUuid());
+        }
+
         // set details map
         if (template.getDetailName() != null) {
             Map<String, String> details = new HashMap<String, String>();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/TemplateJoinVO.java b/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
index 834a9ce..62a103f 100644
--- a/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
@@ -209,6 +209,12 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity {
     @Column(name = "destroyed")
     boolean destroyed = false;
 
+    @Column(name = "parent_template_id")
+    private Long parentTemplateId;
+
+    @Column(name = "parent_template_uuid")
+    private String parentTemplateUuid;
+
     @Column(name = "lp_account_id")
     private Long sharedAccountId;
 
@@ -543,6 +549,14 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity {
         return templateState;
     }
 
+    public Long getParentTemplateId() {
+        return parentTemplateId;
+    }
+
+    public String getParentTemplateUuid() {
+        return parentTemplateUuid;
+    }
+
     @Override
     public Class<?> getEntityType() {
         return VirtualMachineTemplate.class;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5db1bca6/setup/db/db/schema-440to450.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-440to450.sql b/setup/db/db/schema-440to450.sql
index 1fbb312..2493482 100644
--- a/setup/db/db/schema-440to450.sql
+++ b/setup/db/db/schema-440to450.sql
@@ -225,3 +225,106 @@ CREATE VIEW `cloud`.`volume_view` AS
             and async_job.job_status = 0;
 
 ALTER TABLE `cloud`.`vm_template` ADD COLUMN `parent_template_id` bigint(20) unsigned DEFAULT NULL COMMENT 'If datadisk template, then id of the root template this template belongs to';
+
+DROP VIEW IF EXISTS `cloud`.`template_view`;
+CREATE VIEW `cloud`.`template_view` AS
+    select
+        vm_template.id,
+        vm_template.uuid,
+        vm_template.unique_name,
+        vm_template.name,
+        vm_template.public,
+        vm_template.featured,
+        vm_template.type,
+        vm_template.hvm,
+        vm_template.bits,
+        vm_template.url,
+        vm_template.format,
+        vm_template.created,
+        vm_template.checksum,
+        vm_template.display_text,
+        vm_template.enable_password,
+        vm_template.dynamically_scalable,
+        vm_template.state template_state,
+        vm_template.guest_os_id,
+        guest_os.uuid guest_os_uuid,
+        guest_os.display_name guest_os_name,
+        vm_template.bootable,
+        vm_template.prepopulate,
+        vm_template.cross_zones,
+        vm_template.hypervisor_type,
+        vm_template.extractable,
+        vm_template.template_tag,
+        vm_template.sort_key,
+        vm_template.removed,
+        vm_template.enable_sshkey,
+        source_template.id source_template_id,
+        source_template.uuid source_template_uuid,
+        account.id account_id,
+        account.uuid account_uuid,
+        account.account_name account_name,
+        account.type account_type,
+        domain.id domain_id,
+        domain.uuid domain_uuid,
+        domain.name domain_name,
+        domain.path domain_path,
+        projects.id project_id,
+        projects.uuid project_uuid,
+        projects.name project_name,
+        data_center.id data_center_id,
+        data_center.uuid data_center_uuid,
+        data_center.name data_center_name,
+        launch_permission.account_id lp_account_id,
+        parent_template.id parent_template_id,
+        parent_template.uuid parent_template_uuid,
+        template_store_ref.store_id,
+        image_store.scope as store_scope,
+        template_store_ref.state,
+        template_store_ref.download_state,
+        template_store_ref.download_pct,
+        template_store_ref.error_str,
+        template_store_ref.size,
+        template_store_ref.destroyed,
+        template_store_ref.created created_on_store,
+        vm_template_details.name detail_name,
+        vm_template_details.value detail_value,
+        resource_tags.id tag_id,
+        resource_tags.uuid tag_uuid,
+        resource_tags.key tag_key,
+        resource_tags.value tag_value,
+        resource_tags.domain_id tag_domain_id,
+        resource_tags.account_id tag_account_id,
+        resource_tags.resource_id tag_resource_id,
+        resource_tags.resource_uuid tag_resource_uuid,
+        resource_tags.resource_type tag_resource_type,
+        resource_tags.customer tag_customer,
+        CONCAT(vm_template.id, '_', IFNULL(data_center.id, 0)) as temp_zone_pair
+    from
+        `cloud`.`vm_template`
+            left join
+        `cloud`.`guest_os` ON guest_os.id = vm_template.guest_os_id
+            inner join
+        `cloud`.`account` ON account.id = vm_template.account_id
+            inner join
+        `cloud`.`domain` ON domain.id = account.domain_id
+            left join
+        `cloud`.`projects` ON projects.project_account_id = account.id
+            left join
+        `cloud`.`vm_template_details` ON vm_template_details.template_id = vm_template.id
+            left join
+        `cloud`.`vm_template` source_template ON source_template.id = vm_template.source_template_id
+            left join
+        `cloud`.`template_store_ref` ON template_store_ref.template_id = vm_template.id and template_store_ref.store_role = 'Image' and template_store_ref.destroyed=0
+            left join
+        `cloud`.`image_store` ON image_store.removed is NULL AND template_store_ref.store_id is not NULL AND image_store.id = template_store_ref.store_id
+            left join
+        `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id AND template_store_ref.store_id is NULL AND template_zone_ref.removed is null
+            left join
+        `cloud`.`data_center` ON (image_store.data_center_id = data_center.id OR template_zone_ref.zone_id = data_center.id)
+            left join
+        `cloud`.`launch_permission` ON launch_permission.template_id = vm_template.id
+            left join
+        `cloud`.`vm_template` parent_template ON parent_template.id = vm_template.parent_template_id
+            left join
+        `cloud`.`resource_tags` ON resource_tags.resource_id = vm_template.id
+            and (resource_tags.resource_type = 'Template' or resource_tags.resource_type='ISO');


[03/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757 Support OVA with multiple volumes

Get bootdevice from extra config option in ova template.

Signed-off-by: Sateesh Chodapuneedi <sa...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/ac0e7f72
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/ac0e7f72
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/ac0e7f72

Branch: refs/heads/multiple-disk-ova
Commit: ac0e7f722f3fa7aea345c1d94621fd52748365d4
Parents: 6496ccf
Author: Sateesh Chodapuneedi <sa...@apache.org>
Authored: Wed Mar 12 18:06:49 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 13:07:17 2014 +0530

----------------------------------------------------------------------
 .../vmware/mo/HypervisorHostHelper.java         | 85 +++++++++++++++++++-
 1 file changed, 82 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ac0e7f72/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index ab0ab66..28fcd1c 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -51,6 +51,7 @@ import com.vmware.vim25.LongPolicy;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.MethodFault;
 import com.vmware.vim25.ObjectContent;
+import com.vmware.vim25.OptionValue;
 import com.vmware.vim25.OvfCreateDescriptorParams;
 import com.vmware.vim25.OvfCreateDescriptorResult;
 import com.vmware.vim25.OvfCreateImportSpecParams;
@@ -65,12 +66,14 @@ import com.vmware.vim25.VirtualDevice;
 import com.vmware.vim25.VirtualDeviceConfigSpec;
 import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
 import com.vmware.vim25.VirtualDisk;
+import com.vmware.vim25.VirtualIDEController;
 import com.vmware.vim25.VirtualLsiLogicController;
 import com.vmware.vim25.VirtualMachineConfigSpec;
 import com.vmware.vim25.VirtualMachineFileInfo;
 import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
 import com.vmware.vim25.VirtualMachineImportSpec;
 import com.vmware.vim25.VirtualMachineVideoCard;
+import com.vmware.vim25.VirtualSCSIController;
 import com.vmware.vim25.VirtualSCSISharing;
 import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
 import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
@@ -102,6 +105,7 @@ public class HypervisorHostHelper {
     // make vmware-base loosely coupled with cloud-specific stuff, duplicate VLAN.UNTAGGED constant here
     private static final String UNTAGGED_VLAN_NAME = "untagged";
     private static final String VMDK_PACK_DIR = "ova";
+    private static final String OVA_OPTION_KEY_BOOTDISK = "cloud.ova.bootdisk";
 
     public static VirtualMachineMO findVmFromObjectContent(VmwareContext context, ObjectContent[] ocs, String name, String instanceNameCustomField) {
 
@@ -1241,14 +1245,24 @@ public class HypervisorHostHelper {
             files.add(absFile);
         }
 
-        boolean lookForFirstDisk = true;
+        int osDiskSeqNumber = 0;
+        VirtualMachineConfigSpec config = importSpec.getConfigSpec();
+        String paramVal = getOVFParamValue(config);
+        if (paramVal != null && !paramVal.isEmpty()) {
+            try {
+                osDiskSeqNumber = getOsDiskFromOvfConf(config, paramVal);
+            } catch (Exception e) {
+                osDiskSeqNumber = 0;
+            }
+        }
+
         Boolean osDisk = true;
-        List<VirtualDeviceConfigSpec> deviceConfigList = importSpec.getConfigSpec().getDeviceChange();
+        List<VirtualDeviceConfigSpec> deviceConfigList = config.getDeviceChange();
         for (VirtualDeviceConfigSpec deviceSpec : deviceConfigList) {
             VirtualDevice device = deviceSpec.getDevice();
             if (device instanceof VirtualDisk) {
                 sizeKb = ((VirtualDisk)device).getCapacityInKB();
-                if (lookForFirstDisk && diskCount == 0) {
+                if (diskCount == osDiskSeqNumber) {
                     osDisk = true;
                     diskCount++;
                 }
@@ -1259,6 +1273,71 @@ public class HypervisorHostHelper {
         return ovfVolumeInfos;
     }
 
+    public static int getOsDiskFromOvfConf(VirtualMachineConfigSpec config, String deviceLocation) {
+        List<VirtualDeviceConfigSpec> deviceConfigList = config.getDeviceChange();
+        int controllerKey = 0;
+        int deviceSeqNumber = 0;
+        int controllerNumber = 0;
+        int deviceNodeNumber = 0;
+        int controllerCount = 0;
+        String[] virtualNodeInfo = deviceLocation.split(":");
+        if (deviceLocation.startsWith("scsi")) {
+
+            controllerNumber = Integer.parseInt(virtualNodeInfo[0].substring(4)); // get substring excluding prefix scsi
+            deviceNodeNumber = Integer.parseInt(virtualNodeInfo[1]);
+
+            for (VirtualDeviceConfigSpec deviceConfig : deviceConfigList) {
+                VirtualDevice device = deviceConfig.getDevice();
+                if (device instanceof VirtualSCSIController) {
+                    if (controllerNumber == controllerCount) { //((VirtualSCSIController)device).getBusNumber()) {
+                        controllerKey = device.getKey();
+                        break;
+                    }
+                    controllerCount++;
+                }
+            }
+        } else {
+            controllerNumber = Integer.parseInt(virtualNodeInfo[0].substring(3)); // get substring excluding prefix ide
+            deviceNodeNumber = Integer.parseInt(virtualNodeInfo[1]);
+            controllerCount = 0;
+
+            for (VirtualDeviceConfigSpec deviceConfig : deviceConfigList) {
+                VirtualDevice device = deviceConfig.getDevice();
+                if (device instanceof VirtualIDEController) {
+                    if (controllerNumber == controllerCount) { //((VirtualIDEController)device).getBusNumber()) {
+                        // Only 2 IDE controllers supported and they will have bus numbers 0 and 1
+                        controllerKey = device.getKey();
+                        break;
+                    }
+                    controllerCount++;
+                }
+            }
+        }
+        // Get devices on this controller at specific device node.
+        for (VirtualDeviceConfigSpec deviceConfig : deviceConfigList) {
+            VirtualDevice device = deviceConfig.getDevice();
+            if (device instanceof VirtualDisk) {
+                if (controllerKey == device.getControllerKey() && deviceNodeNumber == device.getUnitNumber()) {
+                    break;
+                }
+                deviceSeqNumber++;
+            }
+        }
+        return deviceSeqNumber;
+    }
+
+    public static String getOVFParamValue(VirtualMachineConfigSpec config) {
+        String paramVal = "";
+        List<OptionValue> options = config.getExtraConfig();
+        for (OptionValue option : options) {
+            if (OVA_OPTION_KEY_BOOTDISK.equalsIgnoreCase(option.getKey())) {
+                paramVal = (String)option.getValue();
+                break;
+            }
+        }
+        return paramVal;
+    }
+
     public static void createOvfFile(VmwareHypervisorHost host, String diskFileName, String ovfName, String dir, long size, ManagedObjectReference morDs) throws Exception {
         VmwareContext context = host.getContext();
         VirtualMachineMO workerVmMo = HypervisorHostHelper.createWorkerVM(host, new DatastoreMO(context, morDs), ovfName);


[02/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4787 Support OVA with multiple volumes

Creating OVF file to pack vmdk corresponding to data disk in OVA.

Signed-off-by: Sateesh Chodapuneedi <sa...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/6496ccf1
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/6496ccf1
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/6496ccf1

Branch: refs/heads/multiple-disk-ova
Commit: 6496ccf184da853e93837665f0910fbdd82b6561
Parents: 11c011d
Author: Sateesh Chodapuneedi <sa...@apache.org>
Authored: Wed Mar 12 13:33:04 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 13:07:05 2014 +0530

----------------------------------------------------------------------
 .../vmware/mo/HypervisorHostHelper.java         | 45 ++++++++++++++++++++
 1 file changed, 45 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6496ccf1/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index 8e2254e..ab0ab66 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -17,6 +17,7 @@
 package com.cloud.hypervisor.vmware.mo;
 
 import java.io.File;
+import java.io.FileWriter;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.InvalidParameterException;
@@ -50,8 +51,11 @@ import com.vmware.vim25.LongPolicy;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.MethodFault;
 import com.vmware.vim25.ObjectContent;
+import com.vmware.vim25.OvfCreateDescriptorParams;
+import com.vmware.vim25.OvfCreateDescriptorResult;
 import com.vmware.vim25.OvfCreateImportSpecParams;
 import com.vmware.vim25.OvfCreateImportSpecResult;
+import com.vmware.vim25.OvfFile;
 import com.vmware.vim25.OvfFileItem;
 import com.vmware.vim25.VMwareDVSConfigSpec;
 import com.vmware.vim25.VMwareDVSPortSetting;
@@ -1255,6 +1259,47 @@ public class HypervisorHostHelper {
         return ovfVolumeInfos;
     }
 
+    public static void createOvfFile(VmwareHypervisorHost host, String diskFileName, String ovfName, String dir, long size, ManagedObjectReference morDs) throws Exception {
+        VmwareContext context = host.getContext();
+        VirtualMachineMO workerVmMo = HypervisorHostHelper.createWorkerVM(host, new DatastoreMO(context, morDs), ovfName);
+        if (workerVmMo == null)
+            throw new Exception("Unable to find just-created worker VM");
+
+        String[] disks = {dir + File.separator + diskFileName};
+        boolean bSuccess = false;
+        try {
+            VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
+            VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
+
+            VirtualDevice device = VmwareHelper.prepareDiskDevice(workerVmMo, null, -1, disks, morDs, -1, 1);
+
+            deviceConfigSpec.setDevice(device);
+            deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
+            vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
+            workerVmMo.configureVm(vmConfigSpec);
+            bSuccess = true;
+            OvfFile ovfFile = new OvfFile();
+            ovfFile.setPath(diskFileName);
+            ovfFile.setDeviceId("1000");
+            ovfFile.setSize(size);
+            // write OVF descriptor file
+            OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams();
+            ovfDescParams.getOvfFiles().add(ovfFile);
+            ManagedObjectReference morOvf = context.getServiceContent().getOvfManager();
+            OvfCreateDescriptorResult ovfCreateDescriptorResult = context.getService().createDescriptor(morOvf, workerVmMo.getMor(), ovfDescParams);
+
+            String ovfPath = dir + File.separator + ovfName + ".ovf";
+            FileWriter out = new FileWriter(ovfPath);
+            out.write(ovfCreateDescriptorResult.getOvfDescriptor());
+            out.close();
+        } finally {
+            if (!bSuccess) {
+                workerVmMo.detachAllDisks();
+                workerVmMo.destroy();
+            }
+        }
+    }
+
     public static VirtualMachineMO createWorkerVM(VmwareHypervisorHost hyperHost, DatastoreMO dsMo, String vmName) throws Exception {
 
         // Allow worker VM to float within cluster so that we will have better chance to


[10/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757. Delete template - When delete is called on a template delete all datadisk templates that belong to the template


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/07d514fb
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/07d514fb
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/07d514fb

Branch: refs/heads/multiple-disk-ova
Commit: 07d514fb61d35710c55e67d269af70dde5bac85d
Parents: ad8033f
Author: Likitha Shetty <li...@citrix.com>
Authored: Tue Apr 22 11:37:00 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 17:52:02 2014 +0530

----------------------------------------------------------------------
 .../template/HypervisorTemplateAdapter.java     | 89 +++++++++++++++-----
 1 file changed, 67 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/07d514fb/server/src/com/cloud/template/HypervisorTemplateAdapter.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/com/cloud/template/HypervisorTemplateAdapter.java
index 46f0bd1..f984872 100755
--- a/server/src/com/cloud/template/HypervisorTemplateAdapter.java
+++ b/server/src/com/cloud/template/HypervisorTemplateAdapter.java
@@ -70,6 +70,7 @@ import com.cloud.storage.TemplateProfile;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.VMTemplateZoneVO;
+import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VMTemplateZoneDao;
 import com.cloud.storage.download.DownloadMonitor;
 import com.cloud.user.Account;
@@ -111,6 +112,8 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
     DataCenterDao _dcDao;
     @Inject
     MessageBus _messageBus;
+    @Inject
+    VMTemplateDao _templateDao;
 
     @Override
     public String getName() {
@@ -400,9 +403,10 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
     @Override
     @DB
     public boolean delete(TemplateProfile profile) {
-        boolean success = true;
+        boolean success = false;
 
         VMTemplateVO template = profile.getTemplate();
+        Account account = _accountDao.findByIdIncludingRemoved(template.getAccountId());
 
         // find all eligible image stores for this template
         List<DataStore> imageStores = templateMgr.getImageStoreByTemplate(template.getId(), profile.getZoneId());
@@ -438,29 +442,71 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
                     UsageEventUtils.publishUsageEvent(eventType, template.getAccountId(), sZoneId, template.getId(), null, null, null);
                 }
 
-                s_logger.info("Delete template from image store: " + imageStore.getName());
-                AsyncCallFuture<TemplateApiResult> future = imageService.deleteTemplateAsync(imageFactory.getTemplate(template.getId(), imageStore));
-                try {
-                    TemplateApiResult result = future.get();
-                    success = result.isSuccess();
-                    if (!success) {
-                        s_logger.warn("Failed to delete the template " + template + " from the image store: " + imageStore.getName() + " due to: " + result.getResult());
-                        break;
+                boolean dataDiskDeletetionResult = true;
+                List<VMTemplateVO> dataDiskTemplates = _templateDao.listByParentTemplatetId(template.getId());
+                if (dataDiskTemplates != null && dataDiskTemplates.size() > 0) {
+                    s_logger.info("Template: " + template.getId() + " has Datadisk template(s) associated with it. Delete Datadisk templates before deleting the template");
+                    for (VMTemplateVO dataDiskTemplate : dataDiskTemplates) {
+                        s_logger.info("Delete Datadisk template: " + dataDiskTemplate.getId() + " from image store: " + imageStore.getName());
+                        AsyncCallFuture<TemplateApiResult> future = imageService.deleteTemplateAsync(imageFactory.getTemplate(dataDiskTemplate.getId(), imageStore));
+                        try {
+                            TemplateApiResult result = future.get();
+                            dataDiskDeletetionResult = result.isSuccess();
+                            if (!dataDiskDeletetionResult) {
+                                s_logger.warn("Failed to delete datadisk template: " + dataDiskTemplate + " from image store: " + imageStore.getName() + " due to: "
+                                        + result.getResult());
+                                break;
+                            }
+                            // Remove from template_zone_ref
+                            List<VMTemplateZoneVO> templateZones = templateZoneDao.listByZoneTemplate(sZoneId, dataDiskTemplate.getId());
+                            if (templateZones != null) {
+                                for (VMTemplateZoneVO templateZone : templateZones) {
+                                    templateZoneDao.remove(templateZone.getId());
+                                }
+                            }
+                            // Mark datadisk template as Inactive
+                            List<DataStore> iStores = templateMgr.getImageStoreByTemplate(dataDiskTemplate.getId(), null);
+                            if (iStores == null || iStores.size() == 0) {
+                                dataDiskTemplate.setState(VirtualMachineTemplate.State.Inactive);
+                                _tmpltDao.update(dataDiskTemplate.getId(), dataDiskTemplate);
+                            }
+                            // Decrement total secondary storage space used by the account
+                            _resourceLimitMgr.recalculateResourceCount(dataDiskTemplate.getAccountId(), account.getDomainId(), ResourceType.secondary_storage.getOrdinal());
+                        } catch (Exception e) {
+                            s_logger.debug("Delete datadisk template failed", e);
+                            throw new CloudRuntimeException("Delete datadisk template failed", e);
+                        }
                     }
+                }
 
-                    // remove from template_zone_ref
-                    List<VMTemplateZoneVO> templateZones = templateZoneDao.listByZoneTemplate(sZoneId, template.getId());
-                    if (templateZones != null) {
-                        for (VMTemplateZoneVO templateZone : templateZones) {
-                            templateZoneDao.remove(templateZone.getId());
+                if (dataDiskDeletetionResult) {
+                    s_logger.info("Delete template: " + template.getId() + " from image store: " + imageStore.getName());
+                    AsyncCallFuture<TemplateApiResult> future = imageService.deleteTemplateAsync(imageFactory.getTemplate(template.getId(), imageStore));
+                    try {
+                        TemplateApiResult result = future.get();
+                        success = result.isSuccess();
+                        if (!success) {
+                            s_logger.warn("Failed to delete the template: " + template + " from the image store: " + imageStore.getName() + " due to: " + result.getResult());
+                            break;
                         }
+
+                        // remove from template_zone_ref
+                        List<VMTemplateZoneVO> templateZones = templateZoneDao.listByZoneTemplate(sZoneId, template.getId());
+                        if (templateZones != null) {
+                            for (VMTemplateZoneVO templateZone : templateZones) {
+                                templateZoneDao.remove(templateZone.getId());
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                        s_logger.debug("Delete template Failed", e);
+                        throw new CloudRuntimeException("Delete template Failed", e);
+                    } catch (ExecutionException e) {
+                        s_logger.debug("Delete template Failed", e);
+                        throw new CloudRuntimeException("Delete template Failed", e);
                     }
-                } catch (InterruptedException e) {
-                    s_logger.debug("delete template Failed", e);
-                    throw new CloudRuntimeException("delete template Failed", e);
-                } catch (ExecutionException e) {
-                    s_logger.debug("delete template Failed", e);
-                    throw new CloudRuntimeException("delete template Failed", e);
+                } else {
+                    s_logger.warn("Template: " + template.getId() + " won't be deleted from image store: " + imageStore.getName() + " because deletion of one of the Datadisk" +
+                            " templates that belonged to the template failed");
                 }
             }
         }
@@ -473,7 +519,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
             // delete all cache entries for this template
             List<TemplateInfo> cacheTmpls = imageFactory.listTemplateOnCache(template.getId());
             for (TemplateInfo tmplOnCache : cacheTmpls) {
-                s_logger.info("Delete template from image cache store: " + tmplOnCache.getDataStore().getName());
+                s_logger.info("Delete template: " + tmplOnCache.getId() + " from image cache store: " + tmplOnCache.getDataStore().getName());
                 tmplOnCache.delete();
             }
 
@@ -486,7 +532,6 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
 
                     // Decrement the number of templates and total secondary storage
                     // space used by the account
-                    Account account = _accountDao.findByIdIncludingRemoved(template.getAccountId());
                     _resourceLimitMgr.decrementResourceCount(template.getAccountId(), ResourceType.template);
                     _resourceLimitMgr.recalculateResourceCount(template.getAccountId(), account.getDomainId(), ResourceType.secondary_storage.getOrdinal());
 


[04/13] git commit: updated refs/heads/multiple-disk-ova to 3532ffe

Posted by li...@apache.org.
CLOUDSTACK-4757 Support OVA with multiple volumes

If no user configured bootdisk available, choose 1st disk.

Signed-off-by: Sateesh Chodapuneedi <sa...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/87352b8c
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/87352b8c
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/87352b8c

Branch: refs/heads/multiple-disk-ova
Commit: 87352b8c4ccbba9bb2735764ef81aa6f755d9c5c
Parents: ac0e7f7
Author: Sateesh Chodapuneedi <sa...@apache.org>
Authored: Wed Mar 12 19:14:04 2014 +0530
Committer: Likitha Shetty <li...@citrix.com>
Committed: Fri May 2 13:07:27 2014 +0530

----------------------------------------------------------------------
 .../src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/87352b8c/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index 28fcd1c..8f64a03 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -1264,8 +1264,8 @@ public class HypervisorHostHelper {
                 sizeKb = ((VirtualDisk)device).getCapacityInKB();
                 if (diskCount == osDiskSeqNumber) {
                     osDisk = true;
-                    diskCount++;
                 }
+                diskCount++;
                 ovfVolumeInfo = new Ternary<String, Long, Boolean>(files.get(diskCount), sizeKb, osDisk);
                 ovfVolumeInfos.add(ovfVolumeInfo);
             }