cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From edi...@apache.org
Subject [07/10] add xenserver 6.2.0 hotfix support, to optimize vdi copy
Date Thu, 19 Dec 2013 23:06:50 GMT
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8caf52c6/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java
new file mode 100644
index 0000000..dad3d14
--- /dev/null
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java
@@ -0,0 +1,822 @@
+/*
+ * 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.hypervisor.xen.resource;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.cloudstack.storage.command.CopyCmdAnswer;
+import org.apache.cloudstack.storage.command.CopyCommand;
+import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
+import org.apache.cloudstack.storage.to.SnapshotObjectTO;
+import org.apache.cloudstack.storage.to.TemplateObjectTO;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.to.DataObjectType;
+import com.cloud.agent.api.to.DataStoreTO;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.agent.api.to.NfsTO;
+import com.cloud.agent.api.to.S3TO;
+import com.cloud.agent.api.to.SwiftTO;
+import com.cloud.exception.InternalErrorException;
+import com.cloud.storage.Storage;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.Host;
+import com.xensource.xenapi.PBD;
+import com.xensource.xenapi.SR;
+import com.xensource.xenapi.Task;
+import com.xensource.xenapi.Types;
+import com.xensource.xenapi.VDI;
+
+public class Xenserver625StorageProcessor extends XenServerStorageProcessor {
+    private static final Logger s_logger = Logger.getLogger(XenServerStorageProcessor.class);
+
+    public Xenserver625StorageProcessor(CitrixResourceBase resource) {
+        super(resource);
+    }
+    protected boolean mountNfs(Connection conn, String remoteDir, String localDir) {
+        if (localDir == null) {
+            localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(remoteDir.getBytes());
+        }
+        String results = hypervisorResource.callHostPluginAsync(conn, "cloud-plugin-storage", "mountNfsSecondaryStorage", 100 * 1000,
+                "localDir", localDir, "remoteDir", remoteDir);
+        if (results == null || results.isEmpty()) {
+            String errMsg = "Could not mount secondary storage " + remoteDir + " on host ";
+            s_logger.warn(errMsg);
+            throw new CloudRuntimeException(errMsg);
+        }
+        return true;
+    }
+
+    protected boolean makeDirectory(Connection conn, String path) {
+        String result = hypervisorResource.callHostPlugin(conn, "cloud-plugin-storage", "makeDirectory", "path", path);
+        if (result == null || result.isEmpty()) {
+            return false;
+        }
+        return true;
+    }
+
+    protected SR createFileSR(Connection conn, String path) {
+        SR sr = null;
+        PBD pbd = null;
+        try {
+            Map<String, String> smConfig = new HashMap<String, String>();
+            Host host = Host.getByUuid(conn, hypervisorResource.getHost().uuid);
+            String uuid = UUID.randomUUID().toString();
+
+            sr = SR.introduce(conn,uuid, uuid, uuid, "file", "file", false, smConfig);
+            PBD.Record record = new PBD.Record();
+            record.host = host;
+            record.SR = sr;
+            smConfig.put("location", path);
+            record.deviceConfig = smConfig;
+            pbd = PBD.create(conn, record);
+            pbd.plug(conn);
+            sr.scan(conn);
+            return sr;
+        } catch (Exception e) {
+            try {
+                if (pbd != null) {
+                    pbd.destroy(conn);
+                }
+            } catch (Exception e1) {
+                s_logger.debug("Failed to destroy pbd", e);
+            }
+            try {
+                if (sr != null) {
+                    sr.forget(conn);
+                }
+            } catch (Exception e2) {
+                s_logger.error("Failed to forget sr", e);
+            }
+            String msg = "createFileSR failed! due to " + e.toString();
+            s_logger.warn(msg, e);
+            throw new CloudRuntimeException(msg, e);
+        }
+    }
+
+    protected SR createFileSr(Connection conn, String remotePath, String dir) {
+        String localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(remotePath.getBytes());
+        mountNfs(conn, remotePath, localDir);
+        SR sr = createFileSR(conn, localDir + "/" + dir);
+        return sr;
+    }
+
+    @Override
+    public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) {
+        DataTO srcData = cmd.getSrcTO();
+        DataTO destData = cmd.getDestTO();
+        int wait = cmd.getWait();
+        DataStoreTO srcStore = srcData.getDataStore();
+        Connection conn = hypervisorResource.getConnection();
+        SR srcSr = null;
+        try {
+            if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) {
+                NfsTO srcImageStore = (NfsTO)srcStore;
+                TemplateObjectTO srcTemplate = (TemplateObjectTO)srcData;
+                String storeUrl = srcImageStore.getUrl();
+                URI uri = new URI(storeUrl);
+                String volumePath = srcData.getPath();
+                volumePath = StringUtils.stripEnd(volumePath, "/");
+                String[] splits = volumePath.split("/");
+                String volumeDirectory = volumePath;
+                if (splits.length > 4) {
+                    //"template/tmpl/dcid/templateId/templatename"
+                    int index = volumePath.lastIndexOf("/");
+                    volumeDirectory = volumePath.substring(0, index);
+                }
+                srcSr = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), volumeDirectory);
+                Set<VDI> vdis = srcSr.getVDIs(conn);
+                if (vdis.size() != 1) {
+                    return new CopyCmdAnswer("Can't find template VDI under: " + uri.getHost() + ":" + uri.getPath() + "/" + volumeDirectory);
+                }
+
+                VDI srcVdi = vdis.iterator().next();
+
+                PrimaryDataStoreTO destStore = (PrimaryDataStoreTO)destData.getDataStore();
+                String poolName = destStore.getUuid();
+
+
+                SR poolsr = null;
+                Set<SR> srs = SR.getByNameLabel(conn, poolName);
+                if (srs.size() != 1) {
+                    String msg = "There are " + srs.size() + " SRs with same name: " + poolName;
+                    s_logger.warn(msg);
+                    return new CopyCmdAnswer(msg);
+                } else {
+                    poolsr = srs.iterator().next();
+                }
+                String pUuid = poolsr.getUuid(conn);
+                boolean isISCSI = IsISCSI(poolsr.getType(conn));
+                Task task = srcVdi.copyAsync2(conn, poolsr, null, null);
+                // poll every 1 seconds ,
+                hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                hypervisorResource.checkForSuccess(conn, task);
+                VDI tmpl = Types.toVDI(task, conn);
+                VDI snapshotvdi = tmpl.snapshot(conn, new HashMap<String, String>());
+                snapshotvdi.setNameLabel(conn, "Template " + srcTemplate.getName());
+                tmpl.destroy(conn);
+                poolsr.scan(conn);
+                try{
+                    Thread.sleep(5000);
+                } catch (Exception e) {
+                }
+
+                TemplateObjectTO newVol = new TemplateObjectTO();
+                newVol.setUuid(snapshotvdi.getUuid(conn));
+                newVol.setPath(newVol.getUuid());
+                newVol.setFormat(Storage.ImageFormat.VHD);
+                return new CopyCmdAnswer(newVol);
+            }
+        }catch (Exception e) {
+            String msg = "Catch Exception " + e.getClass().getName() + " for template + " + " due to " + e.toString();
+            s_logger.warn(msg, e);
+            return new CopyCmdAnswer(msg);
+        } finally {
+            if (srcSr != null) {
+                hypervisorResource.removeSR(conn, srcSr);
+            }
+        }
+        return new CopyCmdAnswer("not implemented yet");
+    }
+
+    protected String backupSnapshot(Connection conn, String primaryStorageSRUuid, String localMountPoint, String path, String secondaryStorageMountPath, String snapshotUuid, String prevBackupUuid, String prevSnapshotUuid, Boolean isISCSI, int wait) {
+        String errMsg = null;
+        boolean mounted = false;
+        boolean filesrcreated = false;
+        boolean copied = false;
+        if (prevBackupUuid == null) {
+            prevBackupUuid = "";
+        }
+        SR ssSR = null;
+
+        String remoteDir = secondaryStorageMountPath;
+
+        try {
+            ssSR = createFileSr(conn, remoteDir, path);
+            filesrcreated = true;
+
+            VDI snapshotvdi = VDI.getByUuid(conn, snapshotUuid);
+            Task task = null;
+            if (wait == 0) {
+                wait = 2 * 60 * 60;
+            }
+            VDI dvdi = null;
+            try {
+                VDI previousSnapshotVdi = null;
+                if (prevSnapshotUuid != null) {
+                    previousSnapshotVdi = VDI.getByUuid(conn,prevSnapshotUuid);
+                }
+                task = snapshotvdi.copyAsync2(conn, ssSR, previousSnapshotVdi, null);
+                // poll every 1 seconds ,
+                hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                hypervisorResource.checkForSuccess(conn, task);
+                dvdi = Types.toVDI(task, conn);
+                copied = true;
+            } finally {
+                if (task != null) {
+                    try {
+                        task.destroy(conn);
+                    } catch (Exception e1) {
+                        s_logger.warn("unable to destroy task(" + task.toString() + ") on host("
+                                + ") due to ", e1);
+                    }
+                }
+            }
+            String backupUuid = dvdi.getUuid(conn);
+            return backupUuid;
+        } catch (Exception e) {
+            String msg = "Exception in backupsnapshot stage due to " + e.toString();
+            s_logger.debug(msg);
+            throw new CloudRuntimeException(msg, e);
+        } finally {
+            try {
+                if (filesrcreated && ssSR != null) {
+                    hypervisorResource.removeSR(conn, ssSR);
+                }
+            } catch (Exception e) {
+                s_logger.debug("Exception in backupsnapshot cleanup stage due to " + e.toString());
+            }
+        }
+    }
+
+    @Override
+    protected String getVhdParent(Connection conn, String primaryStorageSRUuid, String snapshotUuid, Boolean isISCSI) {
+        String parentUuid = hypervisorResource.callHostPlugin(conn, "cloud-plugin-storage", "getVhdParent", "primaryStorageSRUuid", primaryStorageSRUuid,
+                "snapshotUuid", snapshotUuid, "isISCSI", isISCSI.toString());
+
+        if (parentUuid == null || parentUuid.isEmpty() || parentUuid.equalsIgnoreCase("None")) {
+            s_logger.debug("Unable to get parent of VHD " + snapshotUuid + " in SR " + primaryStorageSRUuid);
+            // errString is already logged.
+            return null;
+        }
+        return parentUuid;
+    }
+
+    @Override
+    public Answer backupSnapshot(CopyCommand cmd) {
+        Connection conn = hypervisorResource.getConnection();
+        DataTO srcData = cmd.getSrcTO();
+        DataTO cacheData = cmd.getCacheTO();
+        DataTO destData = cmd.getDestTO();
+        int wait = cmd.getWait();
+        PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcData.getDataStore();
+        String primaryStorageNameLabel = primaryStore.getUuid();
+        String secondaryStorageUrl = null;
+        NfsTO cacheStore = null;
+        String destPath = null;
+        if (cacheData != null) {
+            cacheStore = (NfsTO)cacheData.getDataStore();
+            secondaryStorageUrl = cacheStore.getUrl();
+            destPath = cacheData.getPath();
+        } else {
+            cacheStore = (NfsTO)destData.getDataStore();
+            secondaryStorageUrl = cacheStore.getUrl();
+            destPath = destData.getPath();
+        }
+
+        SnapshotObjectTO snapshotTO = (SnapshotObjectTO)srcData;
+        SnapshotObjectTO snapshotOnImage = (SnapshotObjectTO)destData;
+        String snapshotUuid = snapshotTO.getPath();
+
+        String prevBackupUuid = snapshotOnImage.getParentSnapshotPath();
+        String prevSnapshotUuid = snapshotTO.getParentSnapshotPath();
+        Map<String, String> options = cmd.getOptions();
+        // By default assume failure
+        String details = null;
+        String snapshotBackupUuid = null;
+        boolean fullbackup = Boolean.parseBoolean(options.get("fullSnapshot"));
+        try {
+            SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel);
+            if (primaryStorageSR == null) {
+                throw new InternalErrorException("Could not backup snapshot because the primary Storage SR could not be created from the name label: " + primaryStorageNameLabel);
+            }
+            String psUuid = primaryStorageSR.getUuid(conn);
+            Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn));
+
+            VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid);
+            String snapshotPaUuid = null;
+
+            URI uri = new URI(secondaryStorageUrl);
+            String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
+            DataStoreTO destStore = destData.getDataStore();
+            String folder = destPath;
+            String finalPath = null;
+
+            String localMountPoint =  BaseMountPointOnHost + File.separator + UUID.nameUUIDFromBytes(secondaryStorageUrl.getBytes()).toString();
+            if (fullbackup) {
+                SR snapshotSr = null;
+                try {
+                    String localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(secondaryStorageMountPath.getBytes());
+                    mountNfs(conn, secondaryStorageMountPath, localDir);
+                    boolean result = makeDirectory(conn, localDir + "/" + folder);
+                    if (!result) {
+                        details = " Filed to create folder " + folder + " in secondary storage";
+                        s_logger.warn(details);
+                        return new CopyCmdAnswer(details);
+                    }
+
+                    snapshotSr = createFileSr(conn, secondaryStorageMountPath, folder);
+
+                    Task task = snapshotVdi.copyAsync2(conn, snapshotSr, null, null);
+                    // poll every 1 seconds ,
+                    hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                    hypervisorResource.checkForSuccess(conn, task);
+                    VDI backedVdi = Types.toVDI(task, conn);
+                    snapshotBackupUuid = backedVdi.getUuid(conn);
+
+                    if( destStore instanceof SwiftTO) {
+                        try {
+                            String container = "S-" + snapshotTO.getVolume().getVolumeId().toString();
+                            String destSnapshotName = swiftBackupSnapshot(conn, (SwiftTO)destStore, snapshotSr.getUuid(conn), snapshotBackupUuid, container, false, wait);
+                            String swiftPath = container + File.separator + destSnapshotName;
+                            finalPath = swiftPath;
+                        } finally {
+                            try {
+                                deleteSnapshotBackup(conn, localMountPoint, folder, secondaryStorageMountPath, snapshotBackupUuid);
+                            } catch (Exception e) {
+                                s_logger.debug("Failed to delete snapshot on cache storages" ,e);
+                            }
+                        }
+
+                    } else if (destStore instanceof S3TO) {
+                        try {
+                            finalPath = backupSnapshotToS3(conn, (S3TO) destStore, snapshotSr.getUuid(conn), folder, snapshotBackupUuid, isISCSI, wait);
+                            if (finalPath == null) {
+                                throw new CloudRuntimeException("S3 upload of snapshots " + snapshotBackupUuid + " failed");
+                            }
+                        } finally {
+                            try {
+                                deleteSnapshotBackup(conn, localMountPoint, folder, secondaryStorageMountPath, snapshotBackupUuid);
+                            } catch (Exception e) {
+                                s_logger.debug("Failed to delete snapshot on cache storages" ,e);
+                            }
+                        }
+                        // finalPath = folder + File.separator + snapshotBackupUuid;
+                    } else {
+                        finalPath = folder + File.separator + snapshotBackupUuid;
+                    }
+
+                } finally {
+                    if( snapshotSr != null) {
+                        hypervisorResource.removeSR(conn, snapshotSr);
+                    }
+                }
+            } else {
+                String primaryStorageSRUuid = primaryStorageSR.getUuid(conn);
+                if( destStore instanceof SwiftTO ) {
+                    String container = "S-" + snapshotTO.getVolume().getVolumeId().toString();
+                    snapshotBackupUuid = swiftBackupSnapshot(conn, (SwiftTO)destStore, primaryStorageSRUuid, snapshotPaUuid, "S-" + snapshotTO.getVolume().getVolumeId().toString(), isISCSI, wait);
+                    finalPath = container + File.separator + snapshotBackupUuid;
+                } else if (destStore instanceof S3TO ) {
+                    finalPath = backupSnapshotToS3(conn, (S3TO) destStore, primaryStorageSRUuid, folder, snapshotPaUuid, isISCSI, wait);
+                    if (finalPath == null) {
+                        throw new CloudRuntimeException("S3 upload of snapshots " + snapshotPaUuid + " failed");
+                    }
+                } else {
+                    snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, localMountPoint, folder,
+                            secondaryStorageMountPath, snapshotUuid, prevBackupUuid, prevSnapshotUuid, isISCSI, wait);
+
+                    finalPath = folder + File.separator + snapshotBackupUuid;
+                }
+            }
+            String volumeUuid = snapshotTO.getVolume().getPath();
+            destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid);
+
+            SnapshotObjectTO newSnapshot = new SnapshotObjectTO();
+            newSnapshot.setPath(finalPath);
+            if (fullbackup) {
+                newSnapshot.setParentSnapshotPath(null);
+            } else {
+                newSnapshot.setParentSnapshotPath(prevBackupUuid);
+            }
+            return new CopyCmdAnswer(newSnapshot);
+        } catch (Types.XenAPIException e) {
+            details = "BackupSnapshot Failed due to " + e.toString();
+            s_logger.warn(details, e);
+        } catch (Exception e) {
+            details = "BackupSnapshot Failed due to " + e.getMessage();
+            s_logger.warn(details, e);
+        }
+
+        return new CopyCmdAnswer(details);
+    }
+
+    @Override
+    public Answer createTemplateFromVolume(CopyCommand cmd) {
+        Connection conn = hypervisorResource.getConnection();
+        VolumeObjectTO volume = (VolumeObjectTO)cmd.getSrcTO();
+        TemplateObjectTO template = (TemplateObjectTO)cmd.getDestTO();
+        NfsTO destStore = (NfsTO)cmd.getDestTO().getDataStore();
+        int wait = cmd.getWait();
+
+        String secondaryStoragePoolURL = destStore.getUrl();
+        String volumeUUID = volume.getPath();
+
+        String userSpecifiedName = template.getName();
+
+
+        String details = null;
+        SR tmpltSR = null;
+        boolean result = false;
+        String secondaryStorageMountPath = null;
+        String installPath = null;
+        try {
+            URI uri = new URI(secondaryStoragePoolURL);
+            secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath();
+            installPath = template.getPath();
+            if( !hypervisorResource.createSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath)) {
+                details = " Filed to create folder " + installPath + " in secondary storage";
+                s_logger.warn(details);
+                return new CopyCmdAnswer(details);
+            }
+
+            VDI vol = getVDIbyUuid(conn, volumeUUID);
+            // create template SR
+            tmpltSR = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), installPath);
+
+            // copy volume to template SR
+            Task task = vol.copyAsync2(conn, tmpltSR, null, null);
+            // poll every 1 seconds ,
+            hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+            hypervisorResource.checkForSuccess(conn, task);
+            VDI tmpltVDI = Types.toVDI(task, conn);
+            // scan makes XenServer pick up VDI physicalSize
+            tmpltSR.scan(conn);
+            if (userSpecifiedName != null) {
+                tmpltVDI.setNameLabel(conn, userSpecifiedName);
+            }
+
+            String tmpltUUID = tmpltVDI.getUuid(conn);
+            String tmpltFilename = tmpltUUID + ".vhd";
+            long virtualSize = tmpltVDI.getVirtualSize(conn);
+            long physicalSize = tmpltVDI.getPhysicalUtilisation(conn);
+            // create the template.properties file
+            String templatePath = secondaryStorageMountPath + "/" + installPath;
+            result = hypervisorResource.postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, template.getId());
+            if (!result) {
+                throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir");
+            }
+            installPath = installPath + "/" + tmpltFilename;
+            hypervisorResource.removeSR(conn, tmpltSR);
+            tmpltSR = null;
+            TemplateObjectTO newTemplate = new TemplateObjectTO();
+            newTemplate.setPath(installPath);
+            newTemplate.setFormat(Storage.ImageFormat.VHD);
+            newTemplate.setSize(virtualSize);
+            newTemplate.setPhysicalSize(physicalSize);
+            newTemplate.setName(tmpltUUID);
+            CopyCmdAnswer answer = new CopyCmdAnswer(newTemplate);
+            return answer;
+        } catch (Exception e) {
+            if (tmpltSR != null) {
+                hypervisorResource.removeSR(conn, tmpltSR);
+            }
+            if ( secondaryStorageMountPath != null) {
+                hypervisorResource.deleteSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath);
+            }
+            details = "Creating template from volume " + volumeUUID + " failed due to " + e.toString();
+            s_logger.error(details, e);
+        }
+        return new CopyCmdAnswer(details);
+    }
+
+    protected String getSnapshotUuid(String snapshotPath) {
+        int index = snapshotPath.lastIndexOf(File.separator);
+        String snapshotUuid = snapshotPath.substring(index + 1);
+        index = snapshotUuid.lastIndexOf(".");
+        if (index != -1) {
+            snapshotUuid = snapshotUuid.substring(0, index);
+        }
+        return snapshotUuid;
+    }
+
+    @Override
+    public Answer createVolumeFromSnapshot(CopyCommand cmd) {
+        Connection conn = hypervisorResource.getConnection();
+        DataTO srcData = cmd.getSrcTO();
+        SnapshotObjectTO snapshot = (SnapshotObjectTO)srcData;
+        DataTO destData = cmd.getDestTO();
+        PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore();
+        VolumeObjectTO volume = (VolumeObjectTO)destData;
+                DataStoreTO imageStore = srcData.getDataStore();
+
+        if (!(imageStore instanceof NfsTO)) {
+            return new CopyCmdAnswer("unsupported protocol");
+        }
+
+        NfsTO nfsImageStore = (NfsTO)imageStore;
+        String primaryStorageNameLabel = pool.getUuid();
+        String secondaryStorageUrl = nfsImageStore.getUrl();
+        int wait = cmd.getWait();
+        boolean result = false;
+        // Generic error message.
+        String details = null;
+        String volumeUUID = null;
+
+        if (secondaryStorageUrl == null) {
+            details += " because the URL passed: " + secondaryStorageUrl + " is invalid.";
+            return new CopyCmdAnswer(details);
+        }
+        SR srcSr = null;
+        VDI destVdi = null;
+        try {
+            SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel);
+            if (primaryStorageSR == null) {
+                throw new InternalErrorException("Could not create volume from snapshot because the primary Storage SR could not be created from the name label: "
+                        + primaryStorageNameLabel);
+            }
+            String nameLabel = "cloud-" + UUID.randomUUID().toString();
+            destVdi = createVdi(conn, nameLabel, primaryStorageSR, volume.getSize());
+            volumeUUID = destVdi.getUuid(conn);
+            String snapshotInstallPath = snapshot.getPath();
+            int index = snapshotInstallPath.lastIndexOf(File.separator);
+            String snapshotDirectory = snapshotInstallPath.substring(0, index);
+            String snapshotUuid = getSnapshotUuid(snapshotInstallPath);
+
+            URI uri = new URI(secondaryStorageUrl);
+            srcSr = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), snapshotDirectory);
+
+            String[] parents = snapshot.getParents();
+            List<VDI> snapshotChains = new ArrayList<VDI>();
+            if (parents != null) {
+                for(int i = 0; i < parents.length; i++) {
+                    String snChainPath = parents[i];
+                    String uuid = getSnapshotUuid(snChainPath);
+                    VDI chain = VDI.getByUuid(conn, uuid);
+                    snapshotChains.add(chain);
+                }
+            }
+
+            VDI snapshotVdi = VDI.getByUuid(conn, snapshotUuid);
+            snapshotChains.add(snapshotVdi);
+
+            for(VDI snapChain : snapshotChains) {
+                Task task = snapChain.copyAsync2(conn, null, null, destVdi);
+                // poll every 1 seconds ,
+                hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                hypervisorResource.checkForSuccess(conn, task);
+            }
+
+            result = true;
+            destVdi = VDI.getByUuid(conn, volumeUUID);
+            VDI.Record vdir = destVdi.getRecord(conn);
+            VolumeObjectTO newVol = new VolumeObjectTO();
+            newVol.setPath(volumeUUID);
+            newVol.setSize(vdir.virtualSize);
+            return new CopyCmdAnswer(newVol);
+        } catch (Types.XenAPIException e) {
+            details += " due to " + e.toString();
+            s_logger.warn(details, e);
+        } catch (Exception e) {
+            details += " due to " + e.getMessage();
+            s_logger.warn(details, e);
+        } finally {
+            if (srcSr != null) {
+                hypervisorResource.removeSR(conn, srcSr);
+            }
+            if (!result && destVdi != null) {
+                try {
+                    destVdi.destroy(conn);
+                } catch (Exception e) {
+                    s_logger.debug("destroy dest vdi failed", e);
+                }
+            }
+        }
+        if (!result) {
+            // Is this logged at a higher level?
+            s_logger.error(details);
+        }
+
+        // In all cases return something.
+        return new CopyCmdAnswer(details);
+    }
+
+    @Override
+    public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd) {
+        Connection conn = hypervisorResource.getConnection();
+        VolumeObjectTO srcVolume = (VolumeObjectTO)cmd.getSrcTO();
+        VolumeObjectTO destVolume = (VolumeObjectTO)cmd.getDestTO();
+        int wait = cmd.getWait();
+        DataStoreTO destStore = destVolume.getDataStore();
+
+        if (destStore instanceof NfsTO) {
+            SR secondaryStorage = null;
+            try {
+                NfsTO nfsStore = (NfsTO)destStore;
+                URI uri = new URI(nfsStore.getUrl());
+                // Create the volume folder
+                if (!hypervisorResource.createSecondaryStorageFolder(conn, uri.getHost() + ":" + uri.getPath(), destVolume.getPath())) {
+                    throw new InternalErrorException("Failed to create the volume folder.");
+                }
+
+                // Create a SR for the volume UUID folder
+                secondaryStorage = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), destVolume.getPath());
+                // Look up the volume on the source primary storage pool
+                VDI srcVdi = getVDIbyUuid(conn, srcVolume.getPath());
+                // Copy the volume to secondary storage
+                Task task = srcVdi.copyAsync2(conn, secondaryStorage, null, null);
+                // poll every 1 seconds ,
+                hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                hypervisorResource.checkForSuccess(conn, task);
+                VDI destVdi = Types.toVDI(task, conn);
+                String destVolumeUUID = destVdi.getUuid(conn);
+
+                VolumeObjectTO newVol = new VolumeObjectTO();
+                newVol.setPath(destVolume.getPath() + File.separator + destVolumeUUID + ".vhd");
+                newVol.setSize(srcVolume.getSize());
+                return new CopyCmdAnswer(newVol);
+            } catch (Exception e) {
+                s_logger.debug("Failed to copy volume to secondary: " + e.toString());
+                return new CopyCmdAnswer("Failed to copy volume to secondary: " + e.toString());
+            } finally {
+                hypervisorResource.removeSR(conn, secondaryStorage);
+            }
+        }
+        return new CopyCmdAnswer("unsupported protocol");
+    }
+
+    @Override
+    public Answer copyVolumeFromImageCacheToPrimary(CopyCommand cmd) {
+        Connection conn = hypervisorResource.getConnection();
+        DataTO srcData = cmd.getSrcTO();
+        DataTO destData = cmd.getDestTO();
+        int wait = cmd.getWait();
+        VolumeObjectTO srcVolume = (VolumeObjectTO)srcData;
+        VolumeObjectTO destVolume = (VolumeObjectTO)destData;
+        PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)destVolume.getDataStore();
+        DataStoreTO srcStore = srcVolume.getDataStore();
+
+        if (srcStore instanceof NfsTO) {
+            NfsTO nfsStore = (NfsTO)srcStore;
+            String volumePath = srcVolume.getPath();
+            int index = volumePath.lastIndexOf("/");
+            String volumeDirectory = volumePath.substring(0, index);
+            String volumeUuid = volumePath.substring(index + 1);
+            index = volumeUuid.indexOf(".");
+            if (index != -1) {
+                volumeUuid = volumeUuid.substring(0, index);
+            }
+            URI uri = null;
+            try {
+                uri = new URI(nfsStore.getUrl());
+            } catch (Exception e) {
+                return new CopyCmdAnswer(e.toString());
+            }
+            SR srcSr = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), volumeDirectory);
+            try {
+                SR primaryStoragePool = hypervisorResource.getStorageRepository(conn, primaryStore.getUuid());
+                VDI srcVdi = VDI.getByUuid(conn, volumeUuid);
+                Task task = srcVdi.copyAsync2(conn, primaryStoragePool, null, null);
+                // poll every 1 seconds ,
+                hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                hypervisorResource.checkForSuccess(conn, task);
+                VDI destVdi = Types.toVDI(task, conn);
+                VolumeObjectTO newVol = new VolumeObjectTO();
+                newVol.setPath(destVdi.getUuid(conn));
+                newVol.setSize(srcVolume.getSize());
+
+                return new CopyCmdAnswer(newVol);
+            } catch (Exception e) {
+                String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString();
+                s_logger.warn(msg, e);
+                return new CopyCmdAnswer(e.toString());
+            } finally {
+                if (srcSr != null) {
+                    hypervisorResource.removeSR(conn, srcSr);
+                }
+            }
+        }
+
+        s_logger.debug("unsupported protocol");
+        return new CopyCmdAnswer("unsupported protocol");
+    }
+
+    @Override
+    public Answer createTemplateFromSnapshot(CopyCommand cmd) {
+        Connection conn = hypervisorResource.getConnection();
+        DataTO srcData = cmd.getSrcTO();
+        DataTO destData = cmd.getDestTO();
+        int wait = cmd.getWait();
+        SnapshotObjectTO srcObj = (SnapshotObjectTO)srcData;
+        TemplateObjectTO destObj = (TemplateObjectTO)destData;
+        NfsTO srcStore = (NfsTO)srcObj.getDataStore();
+        NfsTO destStore = (NfsTO)destObj.getDataStore();
+
+        URI srcUri = null;
+        URI destUri = null;
+        try {
+            srcUri = new URI(srcStore.getUrl());
+            destUri = new URI(destStore.getUrl());
+        } catch (Exception e) {
+            s_logger.debug("incorrect url", e);
+            return new CopyCmdAnswer("incorrect url" + e.toString());
+        }
+
+        String srcPath = srcObj.getPath();
+        int index = srcPath.lastIndexOf("/");
+        String srcDir = srcPath.substring(0, index);
+        String destDir = destObj.getPath();
+        SR srcSr = null;
+        SR destSr = null;
+        VDI destVdi = null;
+        boolean result = false;
+        try {
+            srcSr = createFileSr(conn, srcUri.getHost() + ":" + srcUri.getPath(), srcDir);
+
+            String destNfsPath = destUri.getHost() + ":" + destUri.getPath();
+            String localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(destNfsPath.getBytes());
+            mountNfs(conn, destUri.getHost() + ":" + destUri.getPath(), localDir);
+            makeDirectory(conn, localDir + "/" + destDir);
+            destSr = createFileSR(conn, localDir + "/" + destDir);
+
+            String nameLabel = "cloud-" + UUID.randomUUID().toString();
+
+            String[] parents = srcObj.getParents();
+            List<VDI> snapshotChains = new ArrayList<VDI>();
+            if (parents != null) {
+                for(int i = 0; i < parents.length; i++) {
+                    String snChainPath = parents[i];
+                    String uuid = getSnapshotUuid(snChainPath);
+                    VDI chain = VDI.getByUuid(conn, uuid);
+                    snapshotChains.add(chain);
+                }
+            }
+            String snapshotUuid = getSnapshotUuid(srcPath);
+            VDI snapshotVdi = VDI.getByUuid(conn, snapshotUuid);
+            snapshotChains.add(snapshotVdi);
+
+            long templateVirtualSize = snapshotChains.get(0).getVirtualSize(conn);
+            destVdi = createVdi(conn, nameLabel, destSr, templateVirtualSize);
+            String destVdiUuid = destVdi.getUuid(conn);
+
+            for(VDI snapChain : snapshotChains) {
+                Task task = snapChain.copyAsync2(conn, null, null, destVdi);
+                // poll every 1 seconds ,
+                hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+                hypervisorResource.checkForSuccess(conn, task);
+            }
+
+            destVdi = VDI.getByUuid(conn, destVdiUuid);
+            String templatePath = destDir + "/" + destVdiUuid + ".vhd";
+            templatePath = templatePath.replaceAll("//","/");
+            TemplateObjectTO newTemplate = new TemplateObjectTO();
+            newTemplate.setPath(templatePath);
+            newTemplate.setFormat(Storage.ImageFormat.VHD);
+            newTemplate.setSize(destVdi.getVirtualSize(conn));
+            newTemplate.setPhysicalSize(destVdi.getPhysicalUtilisation(conn));
+            newTemplate.setName(destVdiUuid);
+
+            result = true;
+            return new CopyCmdAnswer(newTemplate);
+        } catch (Exception e) {
+            s_logger.error("Failed create template from snapshot", e);
+            return new CopyCmdAnswer("Failed create template from snapshot " + e.toString());
+        } finally {
+            if (!result) {
+                if (destVdi != null) {
+                    try {
+                        destVdi.destroy(conn);
+                    } catch (Exception e) {
+                        s_logger.debug("Clean up left over on dest storage failed: ", e);
+                    }
+                }
+            }
+
+            if (destSr != null) {
+                hypervisorResource.removeSR(conn, destSr);
+            }
+
+            if (srcSr != null) {
+                hypervisorResource.removeSR(conn, srcSr);
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8caf52c6/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java
new file mode 100644
index 0000000..2ee45a3
--- /dev/null
+++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java
@@ -0,0 +1,340 @@
+// 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 org.apache.cloudstack.hypervisor.xenserver;
+
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.log4j.Logger;
+import org.apache.xmlrpc.XmlRpcException;
+
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.Event;
+import com.xensource.xenapi.Host;
+import com.xensource.xenapi.Pool;
+import com.xensource.xenapi.Task;
+import com.xensource.xenapi.Types;
+import com.xensource.xenapi.Types.XenAPIException;
+import com.xensource.xenapi.VM;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.ClusterSyncAnswer;
+import com.cloud.agent.api.ClusterSyncCommand;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.hypervisor.xen.resource.XenServer610Resource;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineName;
+
+/**
+ * 
+ * XenServerResourceNewBase is an abstract base class that encapsulates how
+ * CloudStack should interact with XenServer after a special XenServer
+ * 6.2 hotfix.  From here on, every Resource for future versions of
+ * XenServer should use this as the base class.  This base class lessens
+ * the amount of load CloudStack places on Xapi because it doesn't use
+ * polling as a means to collect data and figure out task completion.
+ * 
+ * This base class differs from CitrixResourceBase in the following ways:
+ *   - VM states are detected using Event.from instead of polling.  This
+ *     increases the number of threads CloudStack uses but the threads
+ *     are mostly idle just waiting for events from XenServer.
+ *   - stats are collected through the http interface rather than Xapi plugin.
+ *     This change may be promoted to CitrixResourceBase as it's also possible
+ *     in previous versions of XenServer.
+ *   - Asynchronous task completion is done throught Event.from rather than
+ *     polling.
+ *
+ */
+public class XenServerResourceNewBase extends XenServer610Resource {
+    private static final Logger s_logger = Logger.getLogger(XenServerResourceNewBase.class);
+    protected VmEventListener _listener = null;
+
+    @Override
+    public StartupCommand[] initialize() throws IllegalArgumentException {
+        StartupCommand[] cmds = super.initialize();
+
+        Connection conn = getConnection();
+        Pool pool;
+        try {
+            pool = Pool.getByUuid(conn, _host.pool);
+            Pool.Record poolr = pool.getRecord(conn);
+
+            Host.Record masterRecord = poolr.master.getRecord(conn);
+            if (_host.uuid.equals(masterRecord.uuid)) {
+                _listener = new VmEventListener(true);
+                _listener.start();
+            } else {
+                _listener = new VmEventListener(false);
+            }
+        } catch (XenAPIException e) {
+            throw new CloudRuntimeException("Unable to determine who is the master", e);
+        } catch (XmlRpcException e) {
+            throw new CloudRuntimeException("Unable to determine who is the master", e);
+        }
+        return cmds;
+    }
+
+    protected void waitForTask2(Connection c, Task task, long pollInterval, long timeout) throws XenAPIException, XmlRpcException, TimeoutException {
+        long beginTime = System.currentTimeMillis();
+        if (s_logger.isTraceEnabled()) {
+            s_logger.trace("Task " + task.getNameLabel(c) + " (" + task.getType(c) + ") sent to " + c.getSessionReference() + " is pending completion with a " + timeout +
+                           "ms timeout");
+        }
+        Set<String> classes = new HashSet<String>();
+        classes.add("Task/" + task.toString());
+        String token = "";
+        Double t = new Double(timeout / 1000);
+        while (true) {
+            Map<?, ?> map = Event.properFrom(c, classes, token, t);
+            token = (String)map.get("token");
+            @SuppressWarnings("unchecked")
+            Set<Event.Record> events = (Set<Event.Record>)map.get("events");
+            if (events.size() == 0) {
+                String msg = "Async " + timeout / 1000 + " seconds timeout for task " + task.toString();
+                s_logger.warn(msg);
+                task.cancel(c);
+                throw new TimeoutException(msg);
+            }
+            for (Event.Record rec : events) {
+                if (!(rec.snapshot instanceof Task.Record)) {
+                    if (s_logger.isDebugEnabled()) {
+                        s_logger.debug("Skipping over " + rec);
+                    }
+                    continue;
+                }
+
+                Task.Record taskRecord = (Task.Record)rec.snapshot;
+
+                if (taskRecord.status != Types.TaskStatusType.PENDING) {
+                    if (s_logger.isDebugEnabled()) {
+                        s_logger.debug("Task is done " + taskRecord.status);
+                    }
+                    return;
+                } else {
+                    s_logger.debug("Task is not done " + taskRecord);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected Answer execute(final ClusterSyncCommand cmd) {
+        if (!_listener.isListening()) {
+            return new Answer(cmd);
+        }
+
+        HashMap<String, Ternary<String, VirtualMachine.State, String>> newStates = _listener.getChanges();
+        return new ClusterSyncAnswer(cmd.getClusterId(), newStates);
+    }
+
+    protected class VmEventListener extends Thread {
+        boolean _stop = false;
+        HashMap<String, Ternary<String, VirtualMachine.State, String>> _changes = new HashMap<String, Ternary<String, VirtualMachine.State, String>>();
+        boolean _isMaster;
+        Set<String> _classes;
+        String _token = "";
+
+        public VmEventListener(boolean isMaster) {
+            _isMaster = isMaster;
+            _classes = new HashSet<String>();
+            _classes.add("VM");
+        }
+
+        @Override
+        public void run() {
+            setName("XS-Listener-" + _host.ip);
+            while (!_stop) {
+                try {
+                    Connection conn = getConnection();
+                    Map<?, ?> results;
+                    try {
+                        results = Event.properFrom(conn, _classes, _token, new Double(30));
+                    } catch (Exception e) {
+                        s_logger.error("Retrying the waiting on VM events due to: ", e);
+                        continue;
+                    }
+
+                    _token = (String)results.get("token");
+                    @SuppressWarnings("unchecked")
+                    Set<Event.Record> events = (Set<Event.Record>)results.get("events");
+                    for (Event.Record event : events) {
+                        try {
+                            if (!(event.snapshot instanceof VM.Record)) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("The snapshot is not a VM: " + event);
+                                }
+                                continue;
+                            }
+                            VM.Record vm = (VM.Record)event.snapshot;
+
+                            String hostUuid = null;
+                            if (vm.residentOn != null && !vm.residentOn.toWireString().contains("OpaqueRef:NULL")) {
+                                hostUuid = vm.residentOn.getUuid(conn);
+                            }
+                            recordChanges(conn, vm, hostUuid);
+                        } catch (Exception e) {
+                            s_logger.error("Skipping over " + event, e);
+                        }
+                    }
+                } catch (Throwable th) {
+                    s_logger.error("Exception caught in eventlistener thread: ", th);
+                }
+            }
+        }
+
+        protected void recordChanges(Connection conn, VM.Record rec, String hostUuid) {
+            String vm = rec.nameLabel;
+            if (!VirtualMachineName.isValidCloudStackVmName(vm, _instance)) {
+                s_logger.debug("Skipping over VMs that does not conform to CloudStack naming convention: " + vm);
+                return;
+            }
+
+            VirtualMachine.State currentState = convertToState(rec.powerState);
+            if (vm.startsWith("migrating")) {
+                s_logger.warn("Skipping " + vm + " because it is migrating.");
+                return;
+            }
+
+            if (currentState == VirtualMachine.State.Stopped) {
+                if (s_logger.isTraceEnabled()) {
+                    s_logger.trace("Double check the power state to make sure we got the correct state for " + vm);
+                }
+                currentState = getRealPowerState(conn, vm);
+            }
+
+            boolean updateMap = false;
+            boolean reportChange = false;
+
+            // NOTE: For now we only record change when the VM is stopped.  We don't find out any VMs starting for now.
+            synchronized (_cluster.intern()) {
+                Ternary<String, VirtualMachine.State, String> oldState = s_vms.get(_cluster, vm);
+                if (oldState == null) {
+                    if (s_logger.isTraceEnabled()) {
+                        s_logger.trace("Unable to find " + vm + " from previous map.  Assuming it was in Stopped state.");
+                    }
+                    oldState = new Ternary<String, VirtualMachine.State, String>(null, VirtualMachine.State.Stopped, null);
+                }
+
+                if (s_logger.isTraceEnabled()) {
+                    s_logger.trace(vm + ": current state=" + currentState + ", previous state=" + oldState);
+                }
+
+                if (oldState.second() == VirtualMachine.State.Starting) {
+                    if (currentState == VirtualMachine.State.Running) {
+                        updateMap = true;
+                        reportChange = false;
+                    } else if (currentState == VirtualMachine.State.Stopped) {
+                        updateMap = false;
+                        reportChange = false;
+                    }
+                } else if (oldState.second() == VirtualMachine.State.Migrating) {
+                    updateMap = true;
+                    reportChange = false;
+                } else if (oldState.second() == VirtualMachine.State.Stopping) {
+                    if (currentState == VirtualMachine.State.Stopped) {
+                        updateMap = true;
+                        reportChange = false;
+                    } else if (currentState == VirtualMachine.State.Running) {
+                        updateMap = false;
+                        reportChange = false;
+                    }
+                } else if (oldState.second() != currentState) {
+                    updateMap = true;
+                    reportChange = true;
+                } else if (hostUuid != null && !hostUuid.equals(oldState.first())) {
+                    if (s_logger.isDebugEnabled()) {
+                        s_logger.debug("Detecting " + vm + " moved from " + oldState.first() + " to " + hostUuid);
+                    }
+                    reportChange = true;
+                    updateMap = true;
+                }
+
+                if (updateMap) {
+                    s_vms.put(_cluster, hostUuid, vm, currentState);
+                    if (s_logger.isTraceEnabled()) {
+                        s_logger.trace("Updated " + vm + " to [" + hostUuid + ", " + currentState);
+                    }
+                }
+                if (reportChange) {
+                    Ternary<String, VirtualMachine.State, String> change = _changes.get(vm);
+                    if (hostUuid == null) {
+                        // This is really strange code.  It looks like the sync
+                        // code wants this to be set, which is extremely weird
+                        // for VMs that are dead.  Why would I want to set the
+                        // hostUuid if the VM is stopped.
+                        hostUuid = oldState.first();
+                        if (hostUuid == null) {
+                            hostUuid = _host.uuid;
+                        }
+                    }
+                    if (change == null) {
+                        change = new Ternary<String, VirtualMachine.State, String>(hostUuid, currentState, null);
+                    } else {
+                        change.first(hostUuid);
+                        change.second(currentState);
+                    }
+                    _changes.put(vm, change);
+                }
+            }
+        }
+
+        @Override
+        public void start() {
+            if (_isMaster) {
+                // Throw away the initial set of events because they're history
+                Connection conn = getConnection();
+                Map<?, ?> results;
+                try {
+                    results = Event.properFrom(conn, _classes, _token, new Double(30));
+                } catch (Exception e) {
+                    s_logger.error("Retrying the waiting on VM events due to: ", e);
+                    throw new CloudRuntimeException("Unable to start a listener thread to listen to VM events", e);
+                }
+                _token = (String)results.get("token");
+                s_logger.debug("Starting the event listener thread for " + _host.uuid);
+                super.start();
+            }
+        }
+
+        public boolean isListening() {
+            return _isMaster;
+        }
+
+        public HashMap<String, Ternary<String, VirtualMachine.State, String>> getChanges() {
+            synchronized (_cluster.intern()) {
+                if (_changes.size() == 0) {
+                    return null;
+                }
+                HashMap<String, Ternary<String, VirtualMachine.State, String>> diff = _changes;
+                _changes = new HashMap<String, Ternary<String, VirtualMachine.State, String>>();
+                return diff;
+            }
+        }
+
+        public void signalStop() {
+            _stop = true;
+            interrupt();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8caf52c6/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage
----------------------------------------------------------------------
diff --git a/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage b/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage
new file mode 100644
index 0000000..03a05b7
--- /dev/null
+++ b/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+# 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.
+
+# Version @VERSION@
+#
+# A plugin for executing script needed by vmops cloud 
+
+import os, sys, time
+import XenAPIPlugin
+if os.path.exists("/opt/xensource/sm"):
+    sys.path.extend(["/opt/xensource/sm/", "/usr/local/sbin/", "/sbin/"])
+if os.path.exists("/usr/lib/xcp/sm"):
+    sys.path.extend(["/usr/lib/xcp/sm/", "/usr/local/sbin/", "/sbin/"])
+
+import SR, VDI, SRCommand, util, lvutil
+from util import CommandException
+import vhdutil
+import shutil
+import lvhdutil
+import errno
+import subprocess
+import xs_errors
+import cleanup
+import stat
+import random
+import cloudstack_pluginlib as lib
+import logging
+
+lib.setup_logging("/var/log/cloud/vmops.log")
+
+VHDUTIL = "vhd-util"
+VHD_PREFIX = 'VHD-'
+CLOUD_DIR = '/var/run/cloud_mount'
+
+def echo(fn):
+    def wrapped(*v, **k):
+        name = fn.__name__
+        logging.debug("#### VMOPS enter  %s ####" % name )
+        res = fn(*v, **k)
+        logging.debug("#### VMOPS exit  %s ####" % name )
+        return res
+    return wrapped
+
+def getPrimarySRPath(primaryStorageSRUuid, isISCSI):
+    if isISCSI:
+        primarySRDir = lvhdutil.VG_PREFIX + primaryStorageSRUuid
+        return os.path.join(lvhdutil.VG_LOCATION, primarySRDir)
+    else:
+        return os.path.join(SR.MOUNT_BASE, primaryStorageSRUuid)
+
+def getBackupVHD(UUID):
+    return UUID + '.' + SR.DEFAULT_TAP
+
+def getVHD(UUID, isISCSI):
+    if isISCSI:
+        return VHD_PREFIX + UUID
+    else:
+        return UUID + '.' + SR.DEFAULT_TAP
+
+def getIsTrueString(stringValue):
+    booleanValue = False
+    if (stringValue and stringValue == 'true'):
+        booleanValue = True
+    return booleanValue
+
+def makeUnavailable(uuid, primarySRPath, isISCSI):
+    if not isISCSI:
+        return
+    VHD = getVHD(uuid, isISCSI)
+    path = os.path.join(primarySRPath, VHD)
+    manageAvailability(path, '-an')
+    return
+
+def manageAvailability(path, value):
+    if path.__contains__("/var/run/sr-mount"):
+        return
+    logging.debug("Setting availability of " + path + " to " + value)
+    try:
+        cmd = ['/usr/sbin/lvchange', value, path]
+        util.pread2(cmd)
+    except: #CommandException, (rc, cmdListStr, stderr):
+        #errMsg = "CommandException thrown while executing: " + cmdListStr + " with return code: " + str(rc) + " and stderr: " + stderr
+        errMsg = "Unexpected exception thrown by lvchange"
+        logging.debug(errMsg)
+        if value == "-ay":
+            # Raise an error only if we are trying to make it available.
+            # Just warn if we are trying to make it unavailable after the
+            # snapshot operation is done.
+            raise xs_errors.XenError(errMsg)
+    return
+
+
+def checkVolumeAvailablility(path):
+    try:
+        if not isVolumeAvailable(path):
+            # The VHD file is not available on XenSever. The volume is probably
+            # inactive or detached.
+            # Do lvchange -ay to make it available on XenServer
+            manageAvailability(path, '-ay')
+    except:
+        errMsg = "Could not determine status of ISCSI path: " + path
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+
+    success = False
+    i = 0
+    while i < 6:
+        i = i + 1
+        # Check if the vhd is actually visible by checking for the link
+        # set isISCSI to true
+        success = isVolumeAvailable(path)
+        if success:
+            logging.debug("Made vhd: " + path + " available and confirmed that it is visible")
+            break
+
+        # Sleep for 10 seconds before checking again.
+        time.sleep(10)
+
+    # If not visible within 1 min fail
+    if not success:
+        logging.debug("Could not make vhd: " +  path + " available despite waiting for 1 minute. Does it exist?")
+
+    return success
+
+def isVolumeAvailable(path):
+    # Check if iscsi volume is available on this XenServer.
+    status = "0"
+    try:
+        p = subprocess.Popen(["/bin/bash", "-c", "if [ -L " + path + " ]; then echo 1; else echo 0;fi"], stdout=subprocess.PIPE)
+        status = p.communicate()[0].strip("\n")
+    except:
+        errMsg = "Could not determine status of ISCSI path: " + path
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+
+    return (status == "1")
+
+def scanParent(path):
+    # Do a scan for the parent for ISCSI volumes
+    # Note that the parent need not be visible on the XenServer
+    parentUUID = ''
+    try:
+        lvName = os.path.basename(path)
+        dirname = os.path.dirname(path)
+        vgName = os.path.basename(dirname)
+        vhdInfo = vhdutil.getVHDInfoLVM(lvName, lvhdutil.extractUuid, vgName)
+        parentUUID = vhdInfo.parentUuid
+    except:
+        errMsg = "Could not get vhd parent of " + path
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+    return parentUUID
+
+def getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI):
+    snapshotVHD    = getVHD(snapshotUuid, isISCSI)
+    snapshotPath   = os.path.join(primarySRPath, snapshotVHD)
+
+    baseCopyUuid = ''
+    if isISCSI:
+        checkVolumeAvailablility(snapshotPath)
+        baseCopyUuid = scanParent(snapshotPath)
+    else:
+        baseCopyUuid = getParent(snapshotPath, isISCSI)
+
+    logging.debug("Base copy of snapshotUuid: " + snapshotUuid + " is " + baseCopyUuid)
+    return baseCopyUuid
+
+def getParent(path, isISCSI):
+    parentUUID = ''
+    try :
+        if isISCSI:
+            parentUUID = vhdutil.getParent(path, lvhdutil.extractUuid)
+        else:
+            parentUUID = vhdutil.getParent(path, cleanup.FileVDI.extractUuid)
+    except:
+        errMsg = "Could not get vhd parent of " + path
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+    return parentUUID
+
+def getVhdParent(session, args):
+    logging.debug("getParent with " + str(args))
+    try:
+        primaryStorageSRUuid      = args['primaryStorageSRUuid']
+        snapshotUuid              = args['snapshotUuid']
+        isISCSI                   = getIsTrueString(args['isISCSI'])
+
+        primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI)
+        logging.debug("primarySRPath: " + primarySRPath)
+
+        baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI)
+
+        return  baseCopyUuid
+    except:
+        logging.debug('getVhdParent', exc_info=True)
+        raise xs_errors.XenError("Failed to getVhdParent")
+def makedirs(path):
+    if not os.path.isdir(path):
+        try:
+            os.makedirs(path)
+        except OSError, (errno, strerror):
+            umount(path)
+            if os.path.isdir(path):
+                return
+            errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror
+            logging.debug(errMsg)
+            raise xs_errors.XenError(errMsg)
+    return
+
+def umount(localDir):
+    try:
+        cmd = ['umount', localDir]
+        util.pread2(cmd)
+    except CommandException:
+        errMsg = "CommandException raised while trying to umount " + localDir
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+
+    logging.debug("Successfully unmounted " + localDir)
+    return
+
+@echo
+def mountNfsSecondaryStorage(session, args):
+    remoteDir = args['remoteDir']
+    localDir  = args['localDir']
+    mounted = False
+    f = open("/proc/mounts", 'r')
+    for line in f:
+        tokens = line.split(" ")
+        if len(tokens) > 2 and tokens[0] == remoteDir and tokens[1] == localDir:
+            mounted = True
+
+    if mounted:
+        return "true"
+
+    makedirs(localDir)
+    options = "soft,tcp,timeo=133,retrans=1"
+    try:
+        cmd = ['mount', '-o', options, remoteDir, localDir]
+        txt = util.pread2(cmd)
+    except:
+        txt = ''
+        errMsg = "Unexpected error while trying to mount " + remoteDir + " to " + localDir
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+    logging.debug("Successfully mounted " + remoteDir + " to " + localDir)
+
+    return "true"
+
+@echo
+def umountNfsSecondaryStorage(session, args):
+    localDir = args['localDir']
+    try:
+        cmd = ['umount', localDir]
+        util.pread2(cmd)
+    except CommandException:
+        errMsg = "CommandException raised while trying to umount " + localDir
+        logging.debug(errMsg)
+        raise xs_errors.XenError(errMsg)
+    try:
+        os.system("rmdir " + localDir)
+    except:
+        pass
+    logging.debug("Successfully unmounted " + localDir)
+    return "true"
+
+@echo
+def makeDirectory(session, args):
+    path = args['path']
+    if not os.path.isdir(path):
+        try:
+            os.makedirs(path)
+        except OSError, (errno, strerror):
+            if os.path.isdir(path):
+                return "true"
+            errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror
+            logging.debug(errMsg)
+            raise xs_errors.XenError(errMsg)
+    return "true"
+
+if __name__ == "__main__":
+    XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "mountNfsSecondaryStorage":mountNfsSecondaryStorage,
+        "umountNfsSecondaryStorage":umountNfsSecondaryStorage,
+        "makeDirectory":makeDirectory})
+    
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8caf52c6/scripts/vm/hypervisor/xenserver/xenserver62/patch
----------------------------------------------------------------------
diff --git a/scripts/vm/hypervisor/xenserver/xenserver62/patch b/scripts/vm/hypervisor/xenserver/xenserver62/patch
new file mode 100644
index 0000000..70b86b4
--- /dev/null
+++ b/scripts/vm/hypervisor/xenserver/xenserver62/patch
@@ -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.
+
+# This file specifies the files that need
+# to be transferred over to the XenServer.
+# The format of this file is as follows:
+# [Name of file]=[source path],[file permission],[destination path]
+# [destination path] is required.
+# If [file permission] is missing, 755 is assumed.
+# If [source path] is missing, it looks in the same
+# directory as the patch file.
+# If [source path] starts with '/', then it is absolute path.
+# If [source path] starts with '~', then it is path relative to management server home directory.
+# If [source path] does not start with '/' or '~', then it is relative path to the location of the patch file. 
+vmops=..,0755,/etc/xapi.d/plugins
+vmopspremium=..,0755,/etc/xapi.d/plugins
+xen-ovs-vif-flows.rules=..,0644,/etc/udev/rules.d
+ovs-vif-flows.py=..,0755,/etc/xapi.d/plugins
+cloudstack_plugins.conf=..,0644,/etc/xensource
+cloudstack_pluginlib.py=..,0755,/etc/xapi.d/plugins
+ovstunnel=..,0755,/etc/xapi.d/plugins
+cloud-plugin-storage=,0755,/etc/xapi.d/plugins
+hostvmstats.py=..,0755,/opt/xensource/sm
+systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso
+id_rsa.cloud=../../../systemvm,0600,/root/.ssh
+network_info.sh=..,0755,/opt/cloud/bin
+setupxenserver.sh=..,0755,/opt/cloud/bin
+make_migratable.sh=..,0755,/opt/cloud/bin
+createipAlias.sh=..,0755,/opt/cloud/bin
+deleteipAlias.sh=..,0755,/opt/cloud/bin
+setup_iscsi.sh=..,0755,/opt/cloud/bin
+pingtest.sh=../../..,0755,/opt/cloud/bin
+dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin
+save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin
+call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin
+call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin
+router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin
+cloud-setup-bonding.sh=..,0755,/opt/cloud/bin
+kill_copy_process.sh=..,0755,/opt/cloud/bin
+setup_heartbeat_sr.sh=..,0755,/opt/cloud/bin
+setup_heartbeat_file.sh=..,0755,/opt/cloud/bin
+check_heartbeat.sh=..,0755,/opt/cloud/bin
+xenheartbeat.sh=..,0755,/opt/cloud/bin
+launch_hb.sh=..,0755,/opt/cloud/bin
+vhd-util=..,0755,/opt/cloud/bin
+upgrade_snapshot.sh=..,0755,/opt/cloud/bin
+cloud-clean-vlan.sh=..,0755,/opt/cloud/bin
+cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin
+bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin
+swift=..,0755,/opt/cloud/bin
+swiftxen=..,0755,/etc/xapi.d/plugins
+s3xen=..,0755,/etc/xapi.d/plugins
+add_to_vcpus_params_live.sh=..,0755,/opt/cloud/bin
+ovs-pvlan=..,0755,/etc/xapi.d/plugins
+ovs-pvlan-dhcp-host.sh=../../../network,0755,/opt/cloud/bin
+ovs-pvlan-vm.sh=../../../network,0755,/opt/cloud/bin
+ovs-pvlan-cleanup.sh=../../../network,0755,/opt/cloud/bin
+ovs-get-dhcp-iface.sh=..,0755,/opt/cloud/bin
+ovs-get-bridge.sh=..,0755,/opt/cloud/bin
+cloudlog=..,0644,/etc/logrotate.d

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8caf52c6/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 73f6897..0c58141 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -1109,6 +1109,13 @@ public enum Config {
             "xenserver61",
             "default Xen PV driver version for registered template, valid value:xenserver56,xenserver61 ",
             "xenserver56,xenserver61"),
+    XenServerHotFix("Advanced",
+            ManagementServer.class,
+            Boolean.class,
+            "xen.hotfix.enabled",
+            "false",
+            "Enable/Disable xenserver hot fix",
+            null),
 
     // VMware
     VmwareUseNexusVSwitch(


Mime
View raw message