You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by se...@apache.org on 2014/06/07 10:50:42 UTC
[02/16] Cleanup of Xen and XenServer terms. Cloned xen plugin
creating a xenserver plugin, then removed xen plugin
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
new file mode 100644
index 0000000..e0e2d7b
--- /dev/null
+++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
@@ -0,0 +1,937 @@
+/*
+ * 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.xenserver.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.DiskTO;
+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 " + localDir;
+ 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 {
+ String srname = hypervisorResource.getHost().uuid + path.trim();
+ Set<SR> srs = SR.getByNameLabel(conn, srname);
+ if ( srs != null && !srs.isEmpty()) {
+ return srs.iterator().next();
+ }
+ 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;
+ Task task = 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> setVdis = srcSr.getVDIs(conn);
+
+ if (setVdis.size() != 1) {
+ return new CopyCmdAnswer("Can't find template VDI under: " + uri.getHost() + ":" + uri.getPath() + "/" + volumeDirectory);
+ }
+
+ VDI srcVdi = setVdis.iterator().next();
+
+ boolean managed = false;
+ String storageHost = null;
+ String managedStoragePoolName = null;
+ String managedStoragePoolRootVolumeName = null;
+ String managedStoragePoolRootVolumeSize = null;
+ String chapInitiatorUsername = null;
+ String chapInitiatorSecret = null;
+
+ PrimaryDataStoreTO destStore = (PrimaryDataStoreTO)destData.getDataStore();
+
+ Map<String, String> details = destStore.getDetails();
+
+ if (details != null) {
+ managed = Boolean.parseBoolean(details.get(PrimaryDataStoreTO.MANAGED));
+
+ if (managed) {
+ storageHost = details.get(PrimaryDataStoreTO.STORAGE_HOST);
+ managedStoragePoolName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET);
+ managedStoragePoolRootVolumeName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET_ROOT_VOLUME);
+ managedStoragePoolRootVolumeSize = details.get(PrimaryDataStoreTO.VOLUME_SIZE);
+ chapInitiatorUsername = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_USERNAME);
+ chapInitiatorSecret = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_SECRET);
+ }
+ }
+
+ final SR destSr;
+
+ if (managed) {
+ details = new HashMap<String, String>();
+
+ details.put(DiskTO.STORAGE_HOST, storageHost);
+ details.put(DiskTO.IQN, managedStoragePoolName);
+ details.put(DiskTO.VOLUME_SIZE, managedStoragePoolRootVolumeSize);
+ details.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInitiatorUsername);
+ details.put(DiskTO.CHAP_INITIATOR_SECRET, chapInitiatorSecret);
+
+ destSr = hypervisorResource.prepareManagedSr(conn, details);
+ }
+ else {
+ String srName = destStore.getUuid();
+ Set<SR> srs = SR.getByNameLabel(conn, srName);
+
+ if (srs.size() != 1) {
+ String msg = "There are " + srs.size() + " SRs with same name: " + srName;
+
+ s_logger.warn(msg);
+
+ return new CopyCmdAnswer(msg);
+ } else {
+ destSr = srs.iterator().next();
+ }
+ }
+
+ task = srcVdi.copyAsync(conn, destSr, null, null);
+
+ // poll every 1 seconds ,
+ hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+ hypervisorResource.checkForSuccess(conn, task);
+
+ VDI tmplVdi = Types.toVDI(task, conn);
+
+ final String uuidToReturn;
+
+ if (managed) {
+ uuidToReturn = tmplVdi.getUuid(conn);
+
+ tmplVdi.setNameLabel(conn, managedStoragePoolRootVolumeName);
+ } else {
+ VDI snapshotVdi = tmplVdi.snapshot(conn, new HashMap<String, String>());
+
+ uuidToReturn = snapshotVdi.getUuid(conn);
+
+ snapshotVdi.setNameLabel(conn, "Template " + srcTemplate.getName());
+
+ tmplVdi.destroy(conn);
+ }
+
+ destSr.scan(conn);
+
+ try{
+ Thread.sleep(5000);
+ } catch (Exception e) {
+ }
+
+ TemplateObjectTO newVol = new TemplateObjectTO();
+
+ newVol.setUuid(uuidToReturn);
+ newVol.setPath(uuidToReturn);
+ 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 (task != null) {
+ try {
+ task.destroy(conn);
+ } catch (Exception e) {
+ s_logger.debug("unable to destroy task (" + task.toWireString() + ") due to " + e.toString());
+ }
+ }
+
+ 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) {
+ 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);
+ if (wait == 0) {
+ wait = 2 * 60 * 60;
+ }
+ VDI dvdi = null;
+ Task task = null;
+ try {
+ VDI previousSnapshotVdi = null;
+ if (prevSnapshotUuid != null) {
+ previousSnapshotVdi = VDI.getByUuid(conn,prevSnapshotUuid);
+ }
+ task = snapshotvdi.copyAsync(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 e) {
+ s_logger.warn("unable to destroy task(" + task.toWireString() + ") due to " + e.toString());
+ }
+ }
+ }
+ 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;
+ Task task = 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 = snapshotVdi.copyAsync(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 (task != null) {
+ try {
+ task.destroy(conn);
+ } catch (Exception e) {
+ s_logger.warn("unable to destroy task(" + task.toWireString() + ") due to " + e.toString());
+ }
+ }
+ 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;
+ Task task = 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 = vol.copyAsync(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);
+ } finally {
+ if (task != null) {
+ try {
+ task.destroy(conn);
+ } catch (Exception e) {
+ s_logger.warn("unable to destroy task(" + task.toWireString() + ") due to " + e.toString());
+ }
+ }
+ }
+ 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.copyAsync(conn, null, null, destVdi);
+ // poll every 1 seconds ,
+ hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+ hypervisorResource.checkForSuccess(conn, task);
+ task.destroy(conn);
+ }
+
+ 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;
+ Task task = 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 = srcVdi.copyAsync(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 {
+ if (task != null) {
+ try {
+ task.destroy(conn);
+ } catch (Exception e) {
+ s_logger.warn("unable to destroy task(" + task.toWireString() + ") due to " + e.toString());
+ }
+ }
+ 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);
+ Task task = null;
+ try {
+ SR primaryStoragePool = hypervisorResource.getStorageRepository(conn, primaryStore.getUuid());
+ VDI srcVdi = VDI.getByUuid(conn, volumeUuid);
+ task = srcVdi.copyAsync(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 (task != null) {
+ try {
+ task.destroy(conn);
+ } catch (Exception e) {
+ s_logger.warn("unable to destroy task(" + task.toString() + ") due to " + e.toString());
+ }
+ }
+ 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.copyAsync(conn, null, null, destVdi);
+ // poll every 1 seconds ,
+ hypervisorResource.waitForTask(conn, task, 1000, wait * 1000);
+ hypervisorResource.checkForSuccess(conn, task);
+ task.destroy(conn);
+ }
+
+ 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/a8212d9e/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java
new file mode 100644
index 0000000..def1b05
--- /dev/null
+++ b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java
@@ -0,0 +1,342 @@
+// 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.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.EventBatch;
+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.StartupCommand;
+import com.cloud.hypervisor.xenserver.resource.XenServer620SP1Resource;
+import com.cloud.utils.Pair;
+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 XenServer620SP1Resource {
+ 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);
+
+ //
+ // TODO disable event listener for now. Wait until everything else is ready
+ //
+
+ // _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.toWireString());
+ String token = "";
+ Double t = new Double(timeout / 1000);
+ while (true) {
+ EventBatch map = Event.from(c, classes, token, t);
+ token = map.token;
+ @SuppressWarnings("unchecked")
+ Set<Event.Record> events = map.events;
+ if (events.size() == 0) {
+ String msg = "No event for task " + task.toWireString();
+ 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, ref:" + task.toWireString() + ", UUID:" + taskRecord.uuid + " is done " + taskRecord.status);
+ }
+ return;
+ } else {
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Task: ref:" + task.toWireString() + ", UUID:" + taskRecord.uuid + " progress: " + taskRecord.progress);
+ }
+
+ }
+ }
+ if (System.currentTimeMillis() - beginTime > timeout) {
+ String msg = "Async " + timeout / 1000 + " seconds timeout for task " + task.toString();
+ s_logger.warn(msg);
+ task.cancel(c);
+ throw new TimeoutException(msg);
+ }
+ }
+ }
+
+
+ protected class VmEventListener extends Thread {
+ boolean _stop = false;
+ HashMap<String, Pair<String, VirtualMachine.State>> _changes = new HashMap<String, Pair<String, VirtualMachine.State>>();
+ 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();
+ EventBatch results;
+ try {
+ results = Event.from(conn, _classes, _token, new Double(30));
+ } catch (Exception e) {
+ s_logger.error("Retrying the waiting on VM events due to: ", e);
+ continue;
+ }
+
+ _token = results.token;
+ @SuppressWarnings("unchecked")
+ Set<Event.Record> events = results.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()) {
+ Pair<String, VirtualMachine.State> 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 Pair<String, VirtualMachine.State>(null, VirtualMachine.State.Stopped);
+ }
+
+ 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) {
+ Pair<String, VirtualMachine.State> 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 Pair<String, VirtualMachine.State>(hostUuid, currentState);
+ } 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();
+ EventBatch results;
+ try {
+ results = Event.from(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 = results.token;
+ s_logger.debug("Starting the event listener thread for " + _host.uuid);
+ super.start();
+ }
+ }
+
+ public boolean isListening() {
+ return _isMaster;
+ }
+
+ public HashMap<String, Pair<String, VirtualMachine.State>> getChanges() {
+ synchronized (_cluster.intern()) {
+ if (_changes.size() == 0) {
+ return null;
+ }
+ HashMap<String, Pair<String, VirtualMachine.State>> diff = _changes;
+ _changes = new HashMap<String, Pair<String, VirtualMachine.State>>();
+ return diff;
+ }
+ }
+
+ public void signalStop() {
+ _stop = true;
+ interrupt();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java
new file mode 100644
index 0000000..bec4740
--- /dev/null
+++ b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+public final class XenserverConfigs {
+ public static final String XS620HotFix = "xs620hotfix";
+ public static final String XSHotFix62ESP1 = "0850b186-4d47-11e3-a720-001b2151a503";
+ public static final String XSHotFix62ESP1004 = "996dd2e7-ad95-49cc-a0be-2c9adc4dfb0b";
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
new file mode 100644
index 0000000..975deec
--- /dev/null
+++ b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
@@ -0,0 +1,249 @@
+/*
+ * 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.storage.motion;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
+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.StrategyPriority;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.MigrateWithStorageAnswer;
+import com.cloud.agent.api.MigrateWithStorageCommand;
+import com.cloud.agent.api.MigrateWithStorageCompleteAnswer;
+import com.cloud.agent.api.MigrateWithStorageCompleteCommand;
+import com.cloud.agent.api.MigrateWithStorageReceiveAnswer;
+import com.cloud.agent.api.MigrateWithStorageReceiveCommand;
+import com.cloud.agent.api.MigrateWithStorageSendAnswer;
+import com.cloud.agent.api.MigrateWithStorageSendCommand;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.Host;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+
+@Component
+public class XenServerStorageMotionStrategy implements DataMotionStrategy {
+ private static final Logger s_logger = Logger.getLogger(XenServerStorageMotionStrategy.class);
+ @Inject
+ AgentManager agentMgr;
+ @Inject
+ VolumeDao volDao;
+ @Inject
+ VolumeDataFactory volFactory;
+ @Inject
+ PrimaryDataStoreDao storagePoolDao;
+ @Inject
+ VMInstanceDao instanceDao;
+
+ @Override
+ public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
+ return StrategyPriority.CANT_HANDLE;
+ }
+
+ @Override
+ public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
+ if (srcHost.getHypervisorType() == HypervisorType.XenServer && destHost.getHypervisorType() == HypervisorType.XenServer) {
+ return StrategyPriority.HYPERVISOR;
+ }
+
+ return StrategyPriority.CANT_HANDLE;
+ }
+
+ @Override
+ public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
+ CopyCommandResult result = new CopyCommandResult(null, null);
+ result.setResult("Unsupported operation requested for copying data.");
+ callback.complete(result);
+
+ return null;
+ }
+
+ @Override
+ public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+ Answer answer = null;
+ String errMsg = null;
+ try {
+ VMInstanceVO instance = instanceDao.findById(vmTo.getId());
+ if (instance != null) {
+ if (srcHost.getClusterId().equals(destHost.getClusterId())) {
+ answer = migrateVmWithVolumesWithinCluster(instance, vmTo, srcHost, destHost, volumeMap);
+ } else {
+ answer = migrateVmWithVolumesAcrossCluster(instance, vmTo, srcHost, destHost, volumeMap);
+ }
+ } else {
+ throw new CloudRuntimeException("Unsupported operation requested for moving data.");
+ }
+ } catch (Exception e) {
+ s_logger.error("copy failed", e);
+ errMsg = e.toString();
+ }
+
+ CopyCommandResult result = new CopyCommandResult(null, answer);
+ result.setResult(errMsg);
+ callback.complete(result);
+ return null;
+ }
+
+ private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost, Host destHost, Map<VolumeInfo, DataStore> volumeToPool)
+ throws AgentUnavailableException {
+
+ // Initiate migration of a virtual machine with it's volumes.
+ try {
+ Map<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
+ for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+ VolumeInfo volume = entry.getKey();
+ VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
+ StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
+ volumeToFilerto.put(volumeTo, filerTo);
+ }
+
+ // Migration across cluster needs to be done in three phases.
+ // 1. Send a migrate receive command to the destination host so that it is ready to receive a vm.
+ // 2. Send a migrate send command to the source host. This actually migrates the vm to the destination.
+ // 3. Complete the process. Update the volume details.
+ MigrateWithStorageReceiveCommand receiveCmd = new MigrateWithStorageReceiveCommand(to, volumeToFilerto);
+ MigrateWithStorageReceiveAnswer receiveAnswer = (MigrateWithStorageReceiveAnswer)agentMgr.send(destHost.getId(), receiveCmd);
+ if (receiveAnswer == null) {
+ s_logger.error("Migration with storage of vm " + vm + " to host " + destHost + " failed.");
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+ } else if (!receiveAnswer.getResult()) {
+ s_logger.error("Migration with storage of vm " + vm + " failed. Details: " + receiveAnswer.getDetails());
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost + ". " + receiveAnswer.getDetails());
+ }
+
+ MigrateWithStorageSendCommand sendCmd =
+ new MigrateWithStorageSendCommand(to, receiveAnswer.getVolumeToSr(), receiveAnswer.getNicToNetwork(), receiveAnswer.getToken());
+ MigrateWithStorageSendAnswer sendAnswer = (MigrateWithStorageSendAnswer)agentMgr.send(srcHost.getId(), sendCmd);
+ if (sendAnswer == null) {
+ s_logger.error("Migration with storage of vm " + vm + " to host " + destHost + " failed.");
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+ } else if (!sendAnswer.getResult()) {
+ s_logger.error("Migration with storage of vm " + vm + " failed. Details: " + sendAnswer.getDetails());
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost + ". " + sendAnswer.getDetails());
+ }
+
+ MigrateWithStorageCompleteCommand command = new MigrateWithStorageCompleteCommand(to);
+ MigrateWithStorageCompleteAnswer answer = (MigrateWithStorageCompleteAnswer)agentMgr.send(destHost.getId(), command);
+ if (answer == null) {
+ s_logger.error("Migration with storage of vm " + vm + " failed.");
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+ } else if (!answer.getResult()) {
+ s_logger.error("Migration with storage of vm " + vm + " failed. Details: " + answer.getDetails());
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost + ". " + answer.getDetails());
+ } else {
+ // Update the volume details after migration.
+ updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
+ }
+
+ return answer;
+ } catch (OperationTimedoutException e) {
+ s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
+ throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId());
+ }
+ }
+
+ private Answer migrateVmWithVolumesWithinCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost, Host destHost, Map<VolumeInfo, DataStore> volumeToPool)
+ throws AgentUnavailableException {
+
+ // Initiate migration of a virtual machine with it's volumes.
+ try {
+ Map<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
+ for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+ VolumeInfo volume = entry.getKey();
+ VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
+ StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
+ volumeToFilerto.put(volumeTo, filerTo);
+ }
+
+ MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto);
+ MigrateWithStorageAnswer answer = (MigrateWithStorageAnswer)agentMgr.send(destHost.getId(), command);
+ if (answer == null) {
+ s_logger.error("Migration with storage of vm " + vm + " failed.");
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+ } else if (!answer.getResult()) {
+ s_logger.error("Migration with storage of vm " + vm + " failed. Details: " + answer.getDetails());
+ throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost + ". " + answer.getDetails());
+ } else {
+ // Update the volume details after migration.
+ updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
+ }
+
+ return answer;
+ } catch (OperationTimedoutException e) {
+ s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
+ throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId());
+ }
+ }
+
+ private void updateVolumePathsAfterMigration(Map<VolumeInfo, DataStore> volumeToPool, List<VolumeObjectTO> volumeTos) {
+ for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+ boolean updated = false;
+ VolumeInfo volume = entry.getKey();
+ StoragePool pool = (StoragePool)entry.getValue();
+ for (VolumeObjectTO volumeTo : volumeTos) {
+ if (volume.getId() == volumeTo.getId()) {
+ VolumeVO volumeVO = volDao.findById(volume.getId());
+ Long oldPoolId = volumeVO.getPoolId();
+ volumeVO.setPath(volumeTo.getPath());
+ volumeVO.setFolder(pool.getPath());
+ volumeVO.setPodId(pool.getPodId());
+ volumeVO.setPoolId(pool.getId());
+ volumeVO.setLastPoolId(oldPoolId);
+ volDao.update(volume.getId(), volumeVO);
+ updated = true;
+ break;
+ }
+ }
+
+ if (!updated) {
+ s_logger.error("Volume path wasn't updated for volume " + volume + " after it was migrated.");
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/hypervisors/xenserver/test/com/cloud/ha/XenServerFencerTest.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/test/com/cloud/ha/XenServerFencerTest.java b/plugins/hypervisors/xenserver/test/com/cloud/ha/XenServerFencerTest.java
new file mode 100644
index 0000000..bd1d8f8
--- /dev/null
+++ b/plugins/hypervisors/xenserver/test/com/cloud/ha/XenServerFencerTest.java
@@ -0,0 +1,39 @@
+// 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.ha;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+
+public class XenServerFencerTest {
+
+ @Test
+ public void testSetAndGetName() throws Exception {
+ XenServerFencer xenServerFencer = new XenServerFencer();
+ String name = "name";
+
+ xenServerFencer.setName(name);
+ String actual = xenServerFencer.getName();
+
+ assertEquals(name, actual);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java
new file mode 100644
index 0000000..0c2944c
--- /dev/null
+++ b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.xenserver.resource;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.xmlrpc.XmlRpcException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.Host;
+import com.xensource.xenapi.Types;
+import com.xensource.xenapi.VIF;
+import com.xensource.xenapi.VM;
+import com.xensource.xenapi.XenAPIObject;
+
+import com.cloud.agent.api.ScaleVmAnswer;
+import com.cloud.agent.api.ScaleVmCommand;
+import com.cloud.agent.api.to.IpAddressTO;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase.XsHost;
+
+public class CitrixResourceBaseTest {
+
+ @Spy
+ CitrixResourceBase _resource = new CitrixResourceBase() {
+
+ @Override
+ public ScaleVmAnswer execute(ScaleVmCommand cmd) {
+ return super.execute(cmd);
+ }
+
+ @Override
+ public String callHostPlugin(Connection conn, String plugin, String cmd, String... params) {
+ return "Success";
+ }
+
+ @Override
+ protected void scaleVM(Connection conn, VM vm, VirtualMachineTO vmSpec, Host host) throws Types.XenAPIException, XmlRpcException {
+ _host.speed = 500;
+ super.scaleVM(conn, vm, vmSpec, host);
+ }
+
+ @Override
+ protected boolean isDmcEnabled(Connection conn, Host host) throws Types.XenAPIException, XmlRpcException {
+ return true;
+ }
+ };
+ @Mock
+ XsHost _host;
+ @Mock
+ Host host;
+ @Mock
+ ScaleVmCommand cmd;
+ @Mock
+ VirtualMachineTO vmSpec;
+ @Mock
+ Connection conn;
+ @Mock
+ VM vm;
+
+ @Before
+ public void setup() {
+
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(vmSpec).when(cmd).getVirtualMachine();
+ doReturn("i-2-3-VM").when(vmSpec).getName();
+
+ }
+
+ // Expecting XmlRpcException while trying to get the record of vm using connection
+ @Test(expected = XmlRpcException.class)
+ public void testScaleVMF1() throws Types.BadServerResponse, Types.XenAPIException, XmlRpcException {
+ doReturn(conn).when(_resource).getConnection();
+ Set<VM> vms = mock(Set.class);
+
+ Iterator iter = mock(Iterator.class);
+ doReturn(iter).when(vms).iterator();
+ when(iter.hasNext()).thenReturn(true).thenReturn(false);
+ doReturn(vm).when(iter).next();
+ VM.Record vmr = mock(VM.Record.class);
+ when(vm.getRecord(conn)).thenThrow(new XmlRpcException("XmlRpcException"));
+ when(vm.getRecord(conn)).thenReturn(vmr);
+ vmr.powerState = Types.VmPowerState.RUNNING;
+ vmr.residentOn = mock(Host.class);
+ XenAPIObject object = mock(XenAPIObject.class);
+ doReturn(new String("OpaqueRef:NULL")).when(object).toWireString();
+ doNothing().when(_resource).scaleVM(conn, vm, vmSpec, host);
+
+ _resource.execute(cmd);
+ verify(iter, times(2)).hasNext();
+ verify(iter, times(2)).next();
+
+ }
+
+ // Test to scale vm "i-2-3-VM" cpu-cap disabled
+ @Test
+ public void testScaleVMF2() throws Types.XenAPIException, XmlRpcException {
+
+ when(vm.getMemoryStaticMax(conn)).thenReturn(1073741824L);
+ when(vm.getMemoryStaticMin(conn)).thenReturn(268435456L);
+ doReturn(536870912L).when(vmSpec).getMinRam();
+ doReturn(536870912L).when(vmSpec).getMaxRam();
+ doNothing().when(vm).setMemoryDynamicRange(conn, 536870912L, 536870912L);
+ doReturn(1).when(vmSpec).getCpus();
+ doNothing().when(vm).setVCPUsNumberLive(conn, 1L);
+ doReturn(500).when(vmSpec).getMinSpeed();
+ doReturn(false).when(vmSpec).getLimitCpuUse();
+ Map<String, String> args = mock(HashMap.class);
+ when(host.callPlugin(conn, "vmops", "add_to_VCPUs_params_live", args)).thenReturn("Success");
+ doReturn(null).when(_resource).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+
+ _resource.scaleVM(conn, vm, vmSpec, host);
+
+ verify(vmSpec, times(1)).getLimitCpuUse();
+ verify(_resource, times(1)).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+ }
+
+ // Test to scale vm "i-2-3-VM" cpu-cap enabled
+ @Test
+ public void testScaleVMF3() throws Types.XenAPIException, XmlRpcException {
+
+ when(vm.getMemoryStaticMax(conn)).thenReturn(1073741824L);
+ when(vm.getMemoryStaticMin(conn)).thenReturn(268435456L);
+ doReturn(536870912L).when(vmSpec).getMinRam();
+ doReturn(536870912L).when(vmSpec).getMaxRam();
+ doNothing().when(vm).setMemoryDynamicRange(conn, 536870912L, 536870912L);
+ doReturn(1).when(vmSpec).getCpus();
+ doNothing().when(vm).setVCPUsNumberLive(conn, 1L);
+ doReturn(500).when(vmSpec).getMinSpeed();
+ doReturn(500).when(vmSpec).getMaxSpeed();
+ doReturn(true).when(vmSpec).getLimitCpuUse();
+ doReturn(null).when(_resource).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "cap", "value", "99", "vmname", "i-2-3-VM");
+ Map<String, String> args = mock(HashMap.class);
+ when(host.callPlugin(conn, "vmops", "add_to_VCPUs_params_live", args)).thenReturn("Success");
+ doReturn(null).when(_resource).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+
+ _resource.scaleVM(conn, vm, vmSpec, host);
+
+ verify(vmSpec, times(1)).getLimitCpuUse();
+ verify(_resource, times(1)).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+ verify(_resource, times(1)).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "cap", "value", "99", "vmname", "i-2-3-VM");
+ }
+
+
+ @Test
+ public void testSetNicDevIdIfCorrectVifIsNotNull() throws Exception {
+ IpAddressTO ip = mock(IpAddressTO.class);
+ when(ip.isAdd()).thenReturn(false);
+ VIF correctVif = null;
+ try {
+ _resource.setNicDevIdIfCorrectVifIsNotNull(conn, ip, correctVif);
+ } catch (NullPointerException e) {
+ fail("this test is meant to show that null pointer is not thrown");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/network-elements/dns-notifier/resources/components-example.xml
----------------------------------------------------------------------
diff --git a/plugins/network-elements/dns-notifier/resources/components-example.xml b/plugins/network-elements/dns-notifier/resources/components-example.xml
index 5977387..c6c84f2 100755
--- a/plugins/network-elements/dns-notifier/resources/components-example.xml
+++ b/plugins/network-elements/dns-notifier/resources/components-example.xml
@@ -109,7 +109,7 @@ under the License.
<adapter name="KVMGuru" class="com.cloud.hypervisor.KVMGuru"/>
</adapters>
<adapters key="com.cloud.resource.Discoverer">
- <adapter name="XCP Agent" class="com.cloud.hypervisor.xen.discoverer.XcpServerDiscoverer"/>
+ <adapter name="XCP Agent" class="com.cloud.hypervisor.xenserver.discoverer.XcpServerDiscoverer"/>
<adapter name="SecondaryStorage" class="com.cloud.storage.secondary.SecondaryStorageDiscoverer"/>
<adapter name="KVM Agent" class="com.cloud.hypervisor.kvm.discoverer.KvmServerDiscoverer"/>
<adapter name="Bare Metal Agent" class="com.cloud.baremetal.BareMetalDiscoverer"/>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/network-elements/juniper-contrail/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/network-elements/juniper-contrail/pom.xml b/plugins/network-elements/juniper-contrail/pom.xml
index 8e9866f..1aadd5e 100644
--- a/plugins/network-elements/juniper-contrail/pom.xml
+++ b/plugins/network-elements/juniper-contrail/pom.xml
@@ -46,7 +46,7 @@
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
- <artifactId>cloud-plugin-hypervisor-xen</artifactId>
+ <artifactId>cloud-plugin-hypervisor-xenserver</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a8212d9e/plugins/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 9b391b8..b5e6a61 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -49,7 +49,7 @@
<module>host-allocators/random</module>
<module>dedicated-resources</module>
<module>hypervisors/ovm</module>
- <module>hypervisors/xen</module>
+ <module>hypervisors/xenserver</module>
<module>hypervisors/kvm</module>
<module>event-bus/rabbitmq</module>
<module>event-bus/inmemory</module>