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:36 UTC

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

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();
         }
     }